Web scraping este procesul de extragere a datelor de pe site-uri web.
Înainte de a încerca să răzuiești un site web, trebuie să te asiguri că furnizorul îl permite în termenii lor de serviciu. De asemenea, ar trebui să verificați dacă puteți utiliza în schimb un API.
Răzuirea masivă poate pune un server sub stres, ceea ce poate duce la refuzul de serviciu. Și nu vrei asta.
Conţinut
Cine ar trebui să citească asta?
Acest articol este destinat cititorilor avansați. Se va presupune că sunteți deja familiarizat cu limbajul de programare Python.
Cel puțin ar trebui să înțelegeți înțelegerea listei, managerul de context și funcțiile. De asemenea, ar trebui să știți cum să configurați un mediu virtual.
Vom rula codul pe computerul dvs. local pentru a explora unele site-uri web. Cu câteva modificări, îl puteți face să ruleze și pe un server.
Ce veți afla în acest articol
La sfârșitul acestui articol, veți ști cum să descărcați o pagină web, să o analizați pentru informații interesante și să o formatați într-un format utilizabil pentru procesare ulterioară. Acest lucru este, de asemenea, cunoscut sub numele de ETL.
Acest articol va explica, de asemenea, ce trebuie făcut dacă acel site web folosește JavaScript pentru a reda conținut (cum ar fi React.js sau Angular).
Condiții prealabile
Înainte de a putea începe, vreau să mă asigur că suntem gata de plecare. Vă rugăm să configurați un mediu virtual și să instalați următoarele pachete în acesta:
- beautifulsoup4 (versiunea 4.9.0 la momentul scrierii)
- solicitări (versiunea 2.23.0 la momentul redactării)
- wordcloud (versiunea 1.17.0 la momentul scrierii, opțional)
- seleniu (versiunea 3.141.0 la momentul scrierii, opțional)
Puteți găsi codul pentru acest proiect în acest document depozit git pe GitHub.
Pentru acest exemplu, vom răzuie fișierul Legea fundamentală pentru Republica Federală Germania. (Nu vă faceți griji, le-am verificat Termenii și condițiile. Ele oferă o versiune XML pentru procesarea automată, dar această pagină servește ca exemplu de procesare HTML. Deci ar trebui să fie bine.)
Pasul 1: Descărcați sursa
Primele lucruri mai întâi: creez un fișier urls.txt
deținând toate adresele URL pe care vreau să le descarc:
https://www.gesetze-im-internet.de/gg/art_1.html
https://www.gesetze-im-internet.de/gg/art_2.html
https://www.gesetze-im-internet.de/gg/art_3.html
https://www.gesetze-im-internet.de/gg/art_4.html
https://www.gesetze-im-internet.de/gg/art_5.html
https://www.gesetze-im-internet.de/gg/art_6.html
https://www.gesetze-im-internet.de/gg/art_7.html
https://www.gesetze-im-internet.de/gg/art_8.html
https://www.gesetze-im-internet.de/gg/art_9.html
https://www.gesetze-im-internet.de/gg/art_10.html
https://www.gesetze-im-internet.de/gg/art_11.html
https://www.gesetze-im-internet.de/gg/art_12.html
https://www.gesetze-im-internet.de/gg/art_12a.html
https://www.gesetze-im-internet.de/gg/art_13.html
https://www.gesetze-im-internet.de/gg/art_14.html
https://www.gesetze-im-internet.de/gg/art_15.html
https://www.gesetze-im-internet.de/gg/art_16.html
https://www.gesetze-im-internet.de/gg/art_16a.html
https://www.gesetze-im-internet.de/gg/art_17.html
https://www.gesetze-im-internet.de/gg/art_17a.html
https://www.gesetze-im-internet.de/gg/art_18.html
https://www.gesetze-im-internet.de/gg/art_19.html
Apoi, scriu un pic de cod Python într-un fișier numit scraper.py
pentru a descărca codul HTML al acestor fișiere.
Într-un scenariu real, acest lucru ar fi prea scump și ați folosi în schimb o bază de date. Pentru a simplifica lucrurile, voi descărca fișiere în același director de lângă magazin și le voi folosi numele ca nume de fișier.
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)
Prin descărcarea fișierelor, le pot procesa local cât doresc, fără a fi dependent de un server. Încearcă să fii un bun cetățean web, bine?
Pasul 2: Analizați sursa
Acum că am descărcat fișierele, este timpul să extrag caracteristicile lor interesante. Prin urmare, mă duc la una dintre paginile pe care le-am descărcat, îl deschid într-un browser web și dau clic pe Ctrl-U pentru a-i vedea sursa. Inspectându-l, îmi va arăta structura HTML.
În cazul meu, m-am gândit că vreau textul legii fără niciun markup. Elementul înfășurat are un id container
. Folosind BeautifulSoup pot vedea că o combinație de find
și get_text
voi face ce vreau eu.
Deoarece am un al doilea pas acum, voi refactoriza puțin codul punându-l în funcții și adăugând o CLI minimă.
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)
Acum pot rula codul în trei moduri:
- Fără argumente pentru a rula totul (adică, descărcați toate adresele URL și extrageți-le, apoi salvați pe disc) prin:
python scraper.py
- Cu un argument de
download
și o adresă URL de descărcatpython scraper.py download https://www.gesetze-im-internet.de/gg/art_1.html
. Aceasta nu va procesa fișierul. - Cu un argument de
parse
și o cale de fișier pentru a analiza:python scraper.py art_1.html
. Aceasta va sări peste etapa de descărcare.
Cu asta, lipsește un ultim lucru.
Pasul 3: Formatați sursa pentru procesare ulterioară
Să presupunem că vreau să generez un cloud de cuvinte pentru fiecare articol. Aceasta poate fi o modalitate rapidă de a vă face o idee despre ce este vorba despre un text. Pentru aceasta, instalați pachetul wordcloud
și actualizați fișierul astfel:
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 = 'https://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')
Ce s-a schimbat? Pentru unul, am descărcat un lista cuvintelor de oprire germane de la GitHub. În acest fel, pot elimina cele mai frecvente cuvinte din textul legii descărcate.
Apoi creez o instanță WordCloud cu lista de cuvinte cheie pe care le-am descărcat și textul legii. Acesta va fi transformat într-o imagine cu același nume de bază.
După prima rundă, am descoperit că lista de cuvinte cheie este incompletă. Așa că adaug cuvinte suplimentare pe care vreau să le exclud din imaginea rezultată.
Cu aceasta, partea principală a răzuirii web este completă.
Bonus: Cum rămâne cu SPA-urile?
SPA-urile – sau aplicațiile cu o singură pagină – sunt aplicații web în care întreaga experiență este controlată de JavaScript, care este executat în browser. Ca atare, descărcarea fișierului HTML nu ne aduce departe. Ce ar trebui să facem în schimb?
Vom folosi browserul. Cu Seleniu. Asigura-te ca instalați un driver de asemenea. Descărcați arhiva .tar.gz și despachetați-o în bin
folderul mediului dvs. virtual, astfel încât acesta va fi găsit de Selenium. Acesta este directorul în care puteți găsi fișierul activate
script (pe sisteme GNU / Linux).
De exemplu, folosesc Site web angular Aici. Angular este un popular SPA-Framework scris în JavaScript și garantat că este controlat de acesta pentru moment.
Deoarece codul va fi mai lent, creez un nou fișier numit crawler.py
pentru aceasta. Conținutul arată astfel:
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 = "https://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")
Aici, Python deschide o instanță Firefox, navighează pe site și caută o <article>
element. Copiază textul său într-un dicționar, care este citit în transform
pas și transformat într-un WordCloud în timpul load
.
Atunci când aveți de-a face cu site-uri care necesită JavaScript, este adesea util să îl folosiți Așteaptă și poate fugi chiar execute_script
pentru a amâna la JavaScript, dacă este necesar.
rezumat
Vă mulțumim că ați citit până aici! Să rezumăm ceea ce am învățat acum:
- Cum să răzuiești un site web cu Python’s
requests
pachet. - Cum să îl traduceți într-o structură semnificativă folosind
beautifulsoup
. - Cum să procesați în continuare această structură în ceva cu care puteți lucra.
- Ce trebuie să faceți dacă pagina țintă se bazează pe JavaScript.
Lecturi suplimentare
Dacă vrei să afli mai multe despre mine, poți urmărește-mă pe Twitter sau vizită site-ul meu.
Nu sunt primul care a scris despre Web Scraping aici pe Routech. Yasoob Khalid și Dave Gray au făcut acest lucru și în trecut:


#Cum #Scrape #siteurile #web #Python
Cum Scrape site-urile web cu Python 3