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.

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
urls.txt

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)
scraper.py

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)
scraper.py

Acum pot rula codul în trei moduri:

  1. 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
  2. Cu un argument de download și o adresă URL de descărcat python scraper.py download https://www.gesetze-im-internet.de/gg/art_1.html. Aceasta nu va procesa fișierul.
  3. 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')
scraper.py

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")
crawler.py

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_scriptpentru 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:

  1. Cum să răzuiești un site web cu Python’s requests pachet.
  2. Cum să îl traduceți într-o structură semnificativă folosind beautifulsoup.
  3. Cum să procesați în continuare această structură în ceva cu care puteți lucra.
  4. 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:

Introducere în Web Scraping cu lxml și Python
de Timber.io Introducere în Web Scraping cu lxml și PythonPhoto de Fabian Grohs[https://unsplash.com/photos/dC6Pb2JdAqs?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText] pe Unsplash[https://unsplashcom/search/photos/web?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText…[https://unsplashcom/search/photos/web?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText…
Cum Scrape site urile web cu Python 3
Răzuire web mai bună în Python cu seleniu, supă frumoasă și panda
de Dave Gray Web Scraping Folosind limbajul de programare Python, este posibil să „răzuim” datele de pe web într-un mod rapid și eficient. Scrapingul web este definit ca: & gt; un instrument pentru transformarea datelor nestructurate de pe web în date structurate care pot fi citite de mașini, care este gata pentru analiză. (su …
1611976327 Razuire web mai buna in Python cu seleniu supa frumoasa