Hogyan lehet kaparni weboldalakat a Python 3 segítségével

A webes lekaparás az adatok webhelyekről történő kinyerésének folyamata.

Mielőtt megpróbálná megkaparni a webhelyet, győződjön meg arról, hogy a szolgáltató engedélyezi-e azt a szolgáltatási feltételeiben. Azt is ellenőriznie kell, hogy használhat-e helyette API-t.

A masszív kaparás a szervert nagyon megterheli, ami a szolgáltatás megtagadásához vezethet. És te ezt nem akarod.

Ki olvassa el ezt?

Ez a cikk haladó olvasóknak szól. Feltételezi, hogy Ön már ismeri a Python programozási nyelvet.

Legalább meg kell értenie a lista megértését, a kontextuskezelőt és a funkciókat. Azt is tudni kell, hogyan állítson be egy virtuális környezetet.

Néhány webhely felfedezéséhez futtatjuk a kódot a helyi gépen. Néhány változtatással a szerveren is futtathatóvá teheti.

Amit megtudhat ebben a cikkben

A cikk végén megtudhatja, hogyan tölthet le egy weboldalt, elemezheti érdekes információkért, és felhasználható formátumban formázhatja további feldolgozás céljából. Ez ETL néven is ismert.

Ez a cikk azt is elmagyarázza, mit kell tennie, ha az adott webhely JavaScript használatával jeleníti meg a tartalmat (például React.js vagy Angular).

Előfeltételek

Mielőtt nekilátnék, szeretnék megbizonyosodni arról, hogy készen állunk-e az indulásra. Kérjük, állítson be egy virtuális környezetet, és telepítse a következő csomagokat:

  • beautifulsoup4 (4.9.0 verzió az írás idején)
  • kérések (2.23.0 verzió az írás idején)
  • wordcloud (1.17.0 verzió az íráskor, opcionális)
  • szelén (3.141.0 verzió az íráskor, opcionális)

A projekt kódját a GitHub e git-tárházában találja.

Ennél a példánál megkaparjuk a Németországi Szövetségi Köztársaság alaptörvényét. (Ne aggódjon, ellenőriztem az Általános Szerződési Feltételeiket. XML verziót kínálnak a gépi feldolgozáshoz, de ez az oldal a HTML feldolgozásának példája. Így rendben kell lennie.)

1. lépés: Töltse le a forrást

Először is: létrehozok egy fájlt, urls.txtamely tartalmazza az összes letölteni kívánt URL-t:

//www.gesetze-im-internet.de/gg/art_1.html //www.gesetze-im-internet.de/gg/art_2.html //www.gesetze-im-internet.de/gg/art_3.html //www.gesetze-im-internet.de/gg/art_4.html //www.gesetze-im-internet.de/gg/art_5.html //www.gesetze-im-internet.de/gg/art_6.html //www.gesetze-im-internet.de/gg/art_7.html //www.gesetze-im-internet.de/gg/art_8.html //www.gesetze-im-internet.de/gg/art_9.html //www.gesetze-im-internet.de/gg/art_10.html //www.gesetze-im-internet.de/gg/art_11.html //www.gesetze-im-internet.de/gg/art_12.html //www.gesetze-im-internet.de/gg/art_12a.html //www.gesetze-im-internet.de/gg/art_13.html //www.gesetze-im-internet.de/gg/art_14.html //www.gesetze-im-internet.de/gg/art_15.html //www.gesetze-im-internet.de/gg/art_16.html //www.gesetze-im-internet.de/gg/art_16a.html //www.gesetze-im-internet.de/gg/art_17.html //www.gesetze-im-internet.de/gg/art_17a.html //www.gesetze-im-internet.de/gg/art_18.html //www.gesetze-im-internet.de/gg/art_19.html

Ezután írok egy kis Python-kódot egy fájlba, amelyet scraper.pya fájlok HTML-jének letöltésére hívnak .

Valódi forgatókönyv szerint ez túl drága lenne, és inkább adatbázist használna. A dolgok egyszerűsége érdekében letöltöm a fájlokat az áruház melletti könyvtárba, és a nevüket használom fájlnévként.

from os import path from pathlib import PurePath import requests with open('urls.txt', 'r') as fh: urls = fh.readlines() urls = [url.strip() for url in urls] # strip `\n` for url in urls: file_name = PurePath(url).name file_path = path.join('.', file_name) text = '' try: response = requests.get(url) if response.ok: text = response.text except requests.exceptions.ConnectionError as exc: print(exc) with open(file_path, 'w') as fh: fh.write(text) print('Written to', file_path)

A fájlok letöltésével helyileg feldolgozhatom őket, amennyire csak akarom, anélkül, hogy függnék a szervertől. Próbáljon jó internetes állampolgár lenni, jó?

2. lépés: A forrás elemzése

Most, hogy letöltöttem a fájlokat, itt az ideje, hogy kibontsa érdekes funkcióikat. Ezért az egyik letöltött oldalra lépek, megnyitom egy webböngészőben, és a Ctrl-U megnyomásával megtekinthetem a forrását. Megvizsgálva megmutatja a HTML-szerkezetet.

Az én esetemben arra gondoltam, hogy a törvény szövegét jelölés nélkül szeretném. Az azt beburkoló elem azonosítója container. A BeautifulSoup használatával látom, hogy kombinálom findés get_textmegteszem, amit akarok.

Mivel most van egy második lépésem, egy kicsit átalakítom a kódot a funkciókba helyezéssel és egy minimális CLI hozzáadásával.

from os import path from pathlib import PurePath import sys from bs4 import BeautifulSoup import requests def download_urls(urls, dir): paths = [] for url in urls: file_name = PurePath(url).name file_path = path.join(dir, file_name) text = '' try: response = requests.get(url) if response.ok: text = response.text else: print('Bad response for', url, response.status_code) except requests.exceptions.ConnectionError as exc: print(exc) with open(file_path, 'w') as fh: fh.write(text) paths.append(file_path) return paths def parse_html(path): with open(path, 'r') as fh: content = fh.read() return BeautifulSoup(content, 'html.parser') def download(urls): return download_urls(urls, '.') def extract(path): return parse_html(path) def transform(soup): container = soup.find(id='container') if container is not None: return container.get_text() def load(key, value): d = {} d[key] = value return d def run_single(path): soup = extract(path) content = transform(soup) unserialised = load(path, content.strip() if content is not None else '') return unserialised def run_everything(): l = [] with open('urls.txt', 'r') as fh: urls = fh.readlines() urls = [url.strip() for url in urls] paths = download(urls) for path in paths: print('Written to', path) l.append(run_single(path)) print(l) if __name__ == "__main__": args = sys.argv if len(args) is 1: run_everything() else: if args[1] == 'download': download([args[2]]) print('Done') if args[1] == 'parse': path = args[2] result = run_single(path) print(result) 

Most háromféleképpen tudom futtatni a kódot:

  1. Minden futtatáshoz szükséges argumentum nélkül (vagyis töltse le az összes URL-t és vonja ki, majd mentse lemezre) a következőn keresztül: python scraper.py
  2. A argumentummal downloadés egy letöltendő URL-lel python scraper.py download //www.gesetze-im-internet.de/gg/art_1.html. Ez nem fogja feldolgozni a fájlt.
  3. Egy érv parseés filepath elemezni: python scraper.py art_1.html. Ez kihagyja a letöltési lépést.

Ezzel egy utolsó dolog hiányzik.

3. lépés: Formázza a forrást a további feldolgozáshoz

Tegyük fel, hogy minden cikkhez szeretnék egy szófelhőt generálni. Ez gyors módja lehet annak, hogy képet kapjunk arról, miről szól a szöveg. Ehhez telepítse a csomagot, wordcloudés frissítse a fájlt a következőképpen:

from os import path from pathlib import Path, PurePath import sys from bs4 import BeautifulSoup import requests from wordcloud import WordCloud STOPWORDS_ADDENDUM = [ 'Das', 'Der', 'Die', 'Diese', 'Eine', 'In', 'InhaltsverzeichnisGrundgesetz', 'im', 'Jede', 'Jeder', 'Kein', 'Sie', 'Soweit', 'Über' ] STOPWORDS_FILE_PATH = 'stopwords.txt' STOPWORDS_URL = '//raw.githubusercontent.com/stopwords-iso/stopwords-de/master/stopwords-de.txt' def download_urls(urls, dir): paths = [] for url in urls: file_name = PurePath(url).name file_path = path.join(dir, file_name) text = '' try: response = requests.get(url) if response.ok: text = response.text else: print('Bad response for', url, response.status_code) except requests.exceptions.ConnectionError as exc: print(exc) with open(file_path, 'w') as fh: fh.write(text) paths.append(file_path) return paths def parse_html(path): with open(path, 'r') as fh: content = fh.read() return BeautifulSoup(content, 'html.parser') def download_stopwords(): stopwords = '' try: response = requests.get(STOPWORDS_URL) if response.ok: stopwords = response.text else: print('Bad response for', url, response.status_code) except requests.exceptions.ConnectionError as exc: print(exc) with open(STOPWORDS_FILE_PATH, 'w') as fh: fh.write(stopwords) return stopwords def download(urls): return download_urls(urls, '.') def extract(path): return parse_html(path) def transform(soup): container = soup.find(id='container') if container is not None: return container.get_text() def load(filename, text): if Path(STOPWORDS_FILE_PATH).exists(): with open(STOPWORDS_FILE_PATH, 'r') as fh: stopwords = fh.readlines() else: stopwords = download_stopwords() # Strip whitespace around stopwords = [stopword.strip() for stopword in stopwords] # Extend stopwords with own ones, which were determined after first run stopwords = stopwords + STOPWORDS_ADDENDUM try: cloud = WordCloud(stopwords=stopwords).generate(text) cloud.to_file(filename.replace('.html', '.png')) except ValueError: print('Could not generate word cloud for', key) def run_single(path): soup = extract(path) content = transform(soup) load(path, content.strip() if content is not None else '') def run_everything(): with open('urls.txt', 'r') as fh: urls = fh.readlines() urls = [url.strip() for url in urls] paths = download(urls) for path in paths: print('Written to', path) run_single(path) print('Done') if __name__ == "__main__": args = sys.argv if len(args) is 1: run_everything() else: if args[1] == 'download': download([args[2]]) print('Done') if args[1] == 'parse': path = args[2] run_single(path) print('Done')

Mi változott? Az egyikhez letöltöttem a német kulcsszavak listáját a GitHub-ból. Így kiküszöbölhetem a letöltött törvény szövegéből a leggyakoribb szavakat.

Ezután egy WordCloud-példányt példányosítok a letöltött állítólisták listájával és a törvény szövegével. Kép lesz ugyanolyan alapnevű képpé alakítva.

Az első futtatás után rájöttem, hogy a zárószavak listája hiányos. Tehát további szavakat adok hozzá, amelyeket ki akarok zárni a képből.

Ezzel a webes kaparás fő része befejeződött.

Bónusz: Mi van a SPA-kkal?

A SPA-k - vagy az egyoldalas alkalmazások - olyan webalkalmazások, amelyekben a teljes élményt a böngészőben végrehajtott JavaScript vezérli. Mint ilyen, a HTML fájl letöltése nem vezet el messzire. Mit tegyünk helyette?

Használjuk a böngészőt. Szelénnel. Ne felejtse el telepíteni az illesztőprogramot is. Töltse le a .tar.gz archívumot, és csomagolja ki bina virtuális környezet mappájába, hogy a Selenium megtalálja. Ez az a könyvtár, ahol megtalálhatja a activateszkriptet (GNU / Linux rendszereken).

Példaként itt az Angular weboldalt használom. Az Angular egy népszerű, JavaScript-ben írt SPA-Framework, amely egyelőre garantáltan általa vezérelhető.

Mivel a kód lassabb lesz, létrehozok egy új fájlt, amelyet crawler.pyhívok. A tartalom így néz ki:

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from wordcloud import WordCloud def extract(url): elem = None driver = webdriver.Firefox() driver.get(url) try: found = WebDriverWait(driver, 10).until( EC.visibility_of( driver.find_element(By.TAG_NAME, "article") ) ) # Make a copy of relevant data, because Selenium will throw if # you try to access the properties after the driver quit elem = { "text": found.text } finally: driver.close() return elem def transform(elem): return elem["text"] def load(text, filepath): cloud = WordCloud().generate(text) cloud.to_file(filepath) if __name__ == "__main__": url = "//angular.io/" filepath = "angular.png" elem = extract(url) if elem is not None: text = transform(elem) load(text, filepath) else: print("Sorry, could not extract data")

Itt a Python megnyit egy Firefox-példányt, böngészi a webhelyet és keresi az elemet. transformSzövegét átmásolja egy szótárba, amelyet a lépésben elolvas, és közben WordCloudzá alakítja load.

A nagy execute_scriptböngészőt igénylő webhelyek kezelésekor gyakran hasznos a Waits használata, és szükség esetén akár a JavaScriptre való halasztás is futtatható .

Összegzés

Köszönöm, hogy idáig elolvastad! Összefoglaljuk a most tanultakat:

  1. Hogyan lehet kaparni egy webhelyet a Python requestscsomagjával.
  2. Hogyan lehet értelmes struktúrává lefordítani a beautifulsoup.
  3. Hogyan lehet ezt a struktúrát tovább dolgozni olyanná, amellyel együtt dolgozhat.
  4. Mi a teendő, ha a céloldal a JavaScript-re támaszkodik?

További irodalom

Ha többet szeretne megtudni rólam, kövessen engem a Twitteren, vagy ellátogathat a weboldalamra.

Nem én írtam először az internetes kaparásról a freeCodeCamp-on. Yasoob Khalid és Dave Gray is ezt tette a múltban:

Intro a webes kaparáshoz lxml és Python segítségével a Timber.io által .com / search / photos / web? utm_source = unsplash & utm_medium = referral & utm_content = creditCopyText ... freeCodeCamp.org freeCodeCamp.org Jobb webes lehúzás a Pythonban Selenium, Beautiful Soup és pandák segítségével Dave Gray Web Scraping segítségével A Python programozási nyelv használatával lehetséges Gyors és hatékony módon „kaparja el” az adatokat a webről. A webes kaparás meghatározása a következő:> eszköz a web strukturálatlan adatainak elemzésre kész, géppel olvasható, strukturált adatokká alakítására. (sou… freeCodeCamp.org freeCodeCamp.org