10. gyakorlat¶
A gyakorlat anyaga¶
Projekt struktúráltsága¶
Egy komolyabb projekt általában úgy van struktúrálva, hogy a projekt mappáján belül a projekt neve mappában a forráskód van, míg a tests
mappában vannak a tesztjeink, valahogy így:
az-en-projektem/
├── README.md
├── setup.py
├── az-en-projektem/
│ ├── a_module.py
│ └── another_module.py
├── tests/
│ ├── test_a_module.py
│ └── test_another_module.py
A setup.py
fájlról azonban még nem hallottunk korábban.
Ez az a fájl, ami segítségével a modulunk telepíthető lesz, platformtól függetlenül.
Felfogható úgy, hogy mint a make
Python verziójaként.
Akinek ismerős a make telepítés, az a Python telepítőjével is jóban lesz, elég hasonlóak.
Make telepítés
Telepítés setup.py segítségével
Ám általában ez a telepítési mód kerülendő, a setup.py egy standardizált dolog, a mindenféle csomagtelepítők megértik.
Leggyakoribb használata lehet a pip install ./
.
A telepítés után a kész modulunkat importálhatjuk egyszerűen bármely más projektbe is (az adott környezeten belül).
Illetve, ha parancssori alkalmazásról van szó, akkor simán használhatjuk a modul nevét is, mint telepített alkalmazást.
A setup.py
elkészítéséről és tartalmáról most nem fogunk mélyebben tanulni, akit érdekel, az az alábbi linkeken megteheti:
- https://docs.python.org/3/distutils/setupscript.html
- https://setuptools.readthedocs.io/en/latest/setuptools.html
Amennyire nekünk szükségünk lesz, azt a PyCharm segítségével is legenerálhatjuk, a Tools > Create setup.py menüpont alatt.
Ezt követően érdemes a packages
részt beállítani, ezt kézzel is megtehetjük, vagy használhatunk erre is egy automatizált megoldást.
from setuptools import setup, find_packages
setup(
name='kpo',
version='',
packages=find_packages(exclude=("tests",)),
url='',
license='',
author='',
author_email='',
description='',
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires=">=3.6",
)
Ezen kívül beállíthatunk különböző trove classifier
-eket a projekthez, amik segítségével kategorizálhatjuk a csomagot, mire való, milyen operációs rendszeren fut, stb.
Erre igaziból akkor lehet szükség, szeretnénk a kész csomagot a PyPI oldalon publikálni.
Elérhető classifierek: https://pypi.org/pypi?%3Aaction=list_classifiers
Bővebben a projektünk csomagolásáról: https://packaging.python.org/tutorials/packaging-projects
Tox¶
https://tox.readthedocs.io/en/latest/ Tesztautomatizálás céljából készült modul. Támogatja a különböző tesztkörnyezeteket, tesztfeladatokat.
[tox]
envlist = py38, cover, alma
skipsdist = true
[testenv]
passenv = PYTHONPATH
;whitelist_externals = find
install_command = python -m pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt
commands =
; find . -type f -name "*.py[c|o]" -delete
pytest
[testenv:cover]
deps = pytest-cov
-r{toxinidir}/requirements.txt
whitelist_externals = coverage
skip_install = true
commands =
pytest --cov
coverage report
coverage html
coverage erase
[tox]
envlist = py38,cover,flake8,pylint,bandit,linters
skipsdist = true
[testenv]
passenv = PYTHONPATH
;whitelist_externals = find
install_command = python -m pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt
commands =
; find . -type f -name "*.py[c|o]" -delete
pytest
[testenv:cover]
deps =
{[testenv]deps}
pytest-cov
whitelist_externals = coverage
skip_install = true
commands =
pytest --cov kpo/
coverage report --fail-under=10
coverage html
coverage erase
[testenv:flake8]
deps =
-r{toxinidir}/requirements.txt
flake8
flake8-bugbear
flake8-docstrings>=1.3.1
flake8-typing-imports>=1.1
pep8-naming
commands =
flake8 kpo/ tests/ setup.py
[testenv:bandit]
deps =
{[testenv:flake8]deps}
bandit
commands =
bandit -r kpo/
[testenv:pylint]
skip_install = true
deps =
pyflakes
pylint!=2.5.0
commands =
pylint --rcfile=tox.ini kpo/
[MESSAGES CONTROL]
; C0111 Missing docstring
; I0011: Locally disabling %s
; I0012: Locally enabling %s
disable=I0011,I0012,C0111,W0142,C
[flake8]
ignore = D100,D101,D102,D103,D104,D107,E501
max-complexity = 10
skip_install = true
[testenv:linters]
skip_install = true
deps =
{[testenv:flake8]deps}
{[testenv:pylint]deps}
{[testenv:bandit]deps}
commands =
{[testenv:flake8]commands}
{[testenv:pylint]commands}
{[testenv:bandit]commands}
;[testenv:doc8]
;skip_install = true
;deps =
; sphinx
; doc8
;commands =
; doc8 docs/source/
;[testenv:docs]
;changedir = doc/
;deps =
; -r {toxinidir}/doc/requirements.txt
;commands =
; sphinx-build -W -b html -d _build/doctrees . _build/html
BeautifulSoup 4¶
Hivatalos dokumentáció: https://www.crummy.com/software/BeautifulSoup/bs4/doc/
import bs4
import requests
URL = r"https://www.tvmustra.hu/"
def main():
resp = requests.get(URL)
# ellenőrzések
print(resp.text)
# keresgélés?
# regex?
soup = bs4.BeautifulSoup(resp.text, "html.parser")
print("text", soup.get_text())
print("title", soup.find("title"))
print("title", soup.title) # Nem minden tag érhető el így!
print("title text", soup.find("title").text)
print("title str", soup.find("title").string)
print("Összes kép az oldalon:", len(soup.find_all("img")))
# Tag objektumok
title = soup.find("title")
print("tag neve", title.name)
all_scripts = soup.find_all("script")
elso = all_scripts[1]
print(elso.name)
print(elso['src'])
print(soup.find_all("div", clazz='home_csatorna_musor_blokk'))
print(soup.find_all("div", class_='home_csatorna_musor_blokk'))
print(soup.find_all("div", attrs={'class': 'home_csatorna_musor_blokk'}))
print("---")
for event in soup.find_all("div", attrs={'class': 'home_csatorna_musor_blokk'}):
if 'Hal a tortán' in event['title']:
print(event)
print("Keresett műsor megtalálva!")
title_time = event['title'].replace('\n', ' ')
print(f"-- {event['data-channel']} {title_time}")
if __name__ == '__main__':
main()
Selenium¶
A Selenium alapvetően egy tesztelő keretrendszer, amit regressziós tesztekhez terveztek, és a segítségével különböző forgatókönyveket írunk le, amivel az amúgy kínkeserves kézi tesztelést úszhatjuk meg (amik alapvetően mindig emberi erőforrást igényelnének).
Nagyon sok mindenre jó, többek között:
- Dinamikus oldalak betöltése (mivel gyakorlatilag egy böngészőn keresztül megy a dolog)
- Adatok beírása formokba
- Kattintás, drag and drop
- Különböző várakozás
Tehát gyakorlatilag bármi dolgot le lehet vele programozni, amit kézzel csinálnánk. Azonban a Seleniumot data science szempontból fogjuk megnézni.
pip install selenium
Hivatalos dokumentáció: https://selenium-python.readthedocs.io/installation.html#introduction
Mivel a használatához szükséges egy speciális böngésző, ezért ezt is le kell töltenünk, és be kell állítanunk.
Gecko driver: https://github.com/mozilla/geckodriver/releases
A letöltött drivert ki kell csomagolni, és be kell tenni az útvonalát a PATH
környezeti változóba.
Másik megoldás, hogy webdriver managereket használunk, ami roppant egyszerű és kényelmes megoldás.
A használatához telepítünk kell a manager csomagot, a pip install webdriver-manager
(a csomagról bővebben itt: https://pypi.org/project/webdriver-manager/)
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://google.com")
driver.close()
driver = webdriver.Firefox(executable_path=GeckoDriverManager().install())
driver.get("https://google.com")
driver.close()
import bs4
from selenium import webdriver
URL = r"https://www.netpincer.hu/city/szeged"
def main():
driver = webdriver.Firefox()
driver.get(URL)
html = driver.page_source
soup = bs4.BeautifulSoup(html, "html.parser")
all_restaurants = soup.find("section", attrs={'class': 'vendor-list-section'})
for etterem in all_restaurants.find_all('li'):
piros_sav = etterem.find('div', attrs={'class': 'tag-container'})
if piros_sav:
for kedvezmeny_jelzo in ["kedvezmény", "ingyen", "ajándék"]:
if kedvezmeny_jelzo in piros_sav.text.lower():
print("Kedvezmény van!")
# CSS Selector
# Másolás a böngészőből
fig_caption = etterem.select('a:nth-child(1) > figure:nth-child(1) > figcaption:nth-child(2) > span:nth-child(1) > span:nth-child(1)')
# print(fig_caption)
print(f"-- {fig_caption[0].text} - {piros_sav.text}")
if __name__ == '__main__':
main()
import bs4
import yaml
from selenium import webdriver
# from selenium.webdriver.common.action_chains import ActionChains
# from selenium.webdriver.support.ui import WebDriverWait
# from selenium.webdriver.support import expected_conditions as EC
# from selenium.webdriver.common.by import By
URL = "https://www.coosp.etr.u-szeged.hu/"
with open("credentials.yaml", encoding="utf8") as fp:
conf = yaml.load(fp, Loader=yaml.FullLoader)
def main():
felvett_targyak = dict()
driver = webdriver.Firefox()
driver.get(URL)
username = driver.find_element_by_id("username")
username.send_keys(conf['user']['neptun'])
password = driver.find_element_by_id("password")
password.send_keys(conf['user']['password'])
search_button = driver.find_element_by_xpath("//input[@type='submit' and @value='Belépés']")
search_button.click()
#
# slider = driver.find_element_by_css_selector("div.slider")
# move = ActionChains(driver)
# move.click_and_hold(slider).move_by_offset(200, 0).release().perform()
#
# WebDriverWait(driver, 5).until(
# EC.visibility_of_element_located((By.CSS_SELECTOR, "div.sliderOk"))
# )
# table = driver.find_element_by_css_selector("#resultingTable").get_attribute('outerHTML')
# soup = bs4.BeautifulSoup(table, "html.parser")
html = driver.page_source
soup = bs4.BeautifulSoup(html, "html.parser")
scenes = soup.find("div", attrs={'id': 'scenetreecontainer'})
for scene in scenes.find_all('div', attrs={'class': 'scene'}):
title = scene.find('span', attrs={'class': 'title'})
# print(title)
# print(title['title'])
if title.text not in felvett_targyak:
felvett_targyak[title.text] = 0
felvett_targyak[title.text] += 1
print("Felvett tárgyaim")
for targy, felvetelek_szama in felvett_targyak.items():
if felvetelek_szama > 1:
print(f"-- {targy}, {felvetelek_szama} alkalommal.")
if __name__ == '__main__':
main()
YAML¶
Sokszor előfordul, hogy valamilyen konfigurációt, beállítást, stb. külön fájlba szeretnénk szervezni. Ehhez láttunk már megoldást, például bármilyen fájlt tudunk írni, olvasni. Láttuk a json csomagot, az is teljesen jól használható ilyesmi célokhoz. Egy másik jó megoldás yaml fájlok használata pl. a PyYAML segítségével, amit most megnézünk minimálisan, de természetesen a json is maximálisan jó megoldás lehetne jelen esetben.
pip install pyyaml
Ezt követően olvashatunk, írhatunk yaml fájlokat dictionaryből.
import json
import yaml
my_config = {
"username": "JohnJRambo",
"password": "Rocky4Adrian",
"weapons": ["M60", "Browning M2", "SVD Dragunov", "M72 LAW"],
"personal_info": {
"image": "/home/rambo/img/me_myself_i.jpg",
"adrian": "https://www.youtube.com/watch?v=tad3NI68dKA"
}
}
with open("rambo_data.yaml", mode="w", encoding="utf8") as fp:
yaml.dump(my_config, fp, yaml.CDumper)
with open("demo_yaml.yml", encoding="utf8") as fp:
data = yaml.load(fp, Loader=yaml.FullLoader)
print(json.dumps(data, indent=2))
Bővebb leírás: https://pyyaml.org/wiki/PyYAMLDocumentation
Feladatok¶
- Tetszőleges egyszerű oldal letöltése és információkinyerés bs4-gyel. Az adatokat valamilyen struktúrába tárold is le, dolgozd is fel (adatosztály, dict), végezz rajta adatgyűjtést!
- Tetszőleges dinamikus oldal letöltése és információkinyerés seleniummal. Az adatokat valamilyen struktúrába tárold is le, dolgozd is fel (adatosztály, dict), végezz rajta adatgyűjtést!