How to: Web Scraping Data mit Beautiful Soup

Eine der wichtigen Aufgaben der Data Science ist es erst einmal an Daten zu kommen die man analysieren kann. Häufig werden einem von Auftraggebern bereits bestehende Daten zur Verfügung gestellt. Es kann aber auch vorkommen, dass erst noch Daten erhoben werden müssen. Eine Möglichkeit wie Daten von Webseiten gesammelt werden können möchte ich euch im folgenden erläutern.

In diesem Beitrag werde ich etwas über das Web Scraping schreiben. Dabei handelt es sich um eine Methode mit der Daten aus den HTML Strukturen von Webseiten extrahiert werden. Vorab gibt es hier aber einige Dinge zu beachten.

  1. Wenn möglich nutzt API’s der Anbieter, da diese genau für die Sammlung von Daten vorgesehen sind
  2. Versichert euch, dass die Daten die ihr sammelt auch für eure Zwecke verwendet werden dürfen (ggf. Fragen)
  3.  Überlastet die Webseite des Anbieters nicht mit euren Anfragen. Gerade wenn man eine komplette Webseite durchsucht und z.B. Dateien runterlädt oder die Unterseiten durchsucht, dann können kleinere Webseiten schnell in die Knie gehen. Außerdem könnte bei zu vielen Datenabfragen in zu kurzer Zeit der Admin auf die Idee kommen, dass ihr ein bösartiger Angreifer seid und eure IP blocken.





Ok jetzt ist man vielleicht etwas eingeschüchtert aber diese Punkte sind relativ leicht zu befolgen. Bevor ich nun zur konkreten Umsetzung gehe. Als Entwicklungsumgebung nutze ich das gewohnte Jupyter Notebook und ich werde folgende Librarys verwenden:

  • requests
  • pandas
  • From bs4Import BeautifulSoup

Als Datenquelle nutzen wir Wikipedia.de. Wir wollen dabei die Namen der Widerstandskämpfer aus der Liste extrahieren inkl. Link zu den Website Daten. Diese Daten sollen in einer CSV Datei gespeichert werden. So ein Projekt zum Sammeln von Daten teilt sich dabei in folgende Schritte auf

  • Webseite inspizieren
  • HTML Quelltext laden
  • Daten Scrapen
  • Daten strukturiert speichern

Webseite inspizieren

Zum inspizieren der Webseite muss der Quelltext angeschaut werden. Dabei ist ein grundsätzliches Verständnis von HTML Dateien notwendig. Mithilfe eines Rechtsklicks auf der Webseite und dem Klick auf „Quellcode anzeigen“ wird der entsprechende Quellcode angezeigt. Der kann sehr lang und verschachtelt sein. Dafür gibt im Browser Edge von Microsoft unter dem Reiter „Elemente“ einen Button mit dem die ausgewählten Objekte im Browser im Quelltext markiert werden.

quelltext inspektion

Dabei stellen wir fest, dass die Information in einem <li> Block enthalten ist. Das ist für unser weiteres Vorgehen wichtig.

Kommen wir nun zu dem Part in dem wir uns die Daten Scrapen. Zuerst erstellen wir uns ein neues Python Notebook und laden die benötigten Librarys

import requests
import pandas as pd
from bs4 import BeautifulSoup

Mit der Requests Library kann ich auf die Inhalte einer Webseite zugreifen und z.B. den HTML Code laden. Außerdem können Dateien und Elemente mit so einer Library geladen werden. Pandas nutzen wir um unsere extrahierten Daten in einem strukturierten Dataframe abzulegen und evtl. später in einer Datei zu speichern. Die Library BeautifulSoup ist eine Library um HTML Dateien zu durchsuchen.

Nach dem Laden der Librarys stellen wir nun eine Verbindung zur Webseite her.

url = 'https://de.wikipedia.org/wiki/Liste_von_Widerstandskämpfern_gegen_den_Nationalsozialismus'
response = requests.get(url)

Wenn man sich das Ergebnis anschaut gibt die variable Response bei Erfolg den Wert 200 zurück. MIt der Methode .text wird der enthaltene HTML Text zurück gegeben. Den speichern wir nun in einer neuen Variable soup mithilfe der Beautifulsoup Library

soup = BeautifulSoup(response.text, "html.parser")

Wer sich die Variable nun ausgeben lässt, kriegt den HTML Quelltext der Webseite zurück. Nun könnte man hier den Text inspizieren, ich empfehle aber den weiter oben genannten Weg über die Quelltext inspizieren Funktion des Browsers. Dabei hatten wir festgestellt, dass die wichtigen Daten in <li> Blöcken enthalten sind. Also durchsuchen wir alle <li> Blöcke und gehen dort durch die Link Elemente. Dann schreiben wir uns die Informationen weg und speichern das Ergebnis in einem Dataframe wie im Codebeispiel beschrieben.

# create a list
data_dict = []
# iterate all rows
for one_tag in soup.findAll(['li']):
    children = one_tag.findChildren('a', recursive = True)
    # iterate all elements per row
    for i in range(len(children)):
        row_dict = {}
# get first link entry 
        if i == 0 and children[i].has_attr('href'):
            col_name = 'link'
            if children[i]['href'][0:4] != 'http':
                col_value = 'http://wikipedia.de' + children[i]['href']
            else:
                col_value = children[i]['href']
            row_dict[col_name] = col_value
# get the name entry
            if children[i].has_attr('title'):
                col_name = 'name'
                col_value = children[i]['title']
                row_dict[col_name] = col_value
# save data in a list
    if len(row_dict) > 0:
        data_dict.append(row_dict)

# transform list to dataframe
df = pd.DataFrame(data_dict)

Gut. Das müssen wir uns eventuell mal im Detail anschauen.
Zuerst erzeugen wir eine Liste die data_dict heißt. Diese ist zu Beginn noch leer. Dann erzeugen wir eine for schleife. Dabei iterieren wir über alle Elemente in unserer Variable soup. Dabei haben wir noch die Funktion .findAll() angewendet. Diese filtert den HTML Text auf alle Objekte die den übergebenen Parametern entsprechen und erstellt eine Liste dieser Objekte. In unserem Beispiel alle <li> Objekte. In der Variable one_tag ist dann der gesamte Inhalt eines <li> Blocks enthalten und ein <li> Block enthält für einen Dateneintrag alle Informationen die wir benötigen. Also suchen uns nun noch die Link Informationen. Diese stehen in <a> Blöcken in der <li> Block Anweisung.

Also durchsuchen wir auch die <li> Blöcke nach den Anweisungen mit der Funktion .findChildren() und iterien über die einzelnen Link Elemente. Wir erzeugen ein Dictionary row_dict in dem wir die Keys (Überschriften) und Values (Einträge) speichern können. Nun speichern wir uns die Daten noch weg. Den Link kriegen wir aus der Anweisung href in dem <a> Block. Ob diese Anweisung im Block vorhanden ist, lässt sich mit einer If Anweisung und der Funktion .hasAttr() prüfen.  dann fügen wir unserem Dictionary einen neuen Eintrag hinzu. der Key ist immer „link“ und das dazugehörige Value Paar ist  immer die jeweilige Linkadresse. Genau so wird auch der Name weggespeicherrt. Dabei ist der Key immer „name“ und der entsprechende Linktitel das Value Paar.

Damit haben wir nun eine Datenzeile in unserem row_dict. Das hängen wir am Ende noch an unser data_dict mit der Methode .append() und ganz am Ende wird diese Liste in einem Dataframe gespeichert, mit welchem man nun weitere Dinge wie Analysen, Datenaufbereitung oder ein Wegspeichern der Daten realisieren kann.

Ich hoffe ich konnte euch einen kleinen Einblick in das Thema Web Scraping geben und einen Ansatz für euer eigenes kleines Projekt.