Webscraping in Python 3: Wie ich es mache

Eine alte Datenjournalistenregel besagt: Wenn Du es einem Praktikanten geben willst, schreib einen Scraper. Stimmt nicht immer, aber oft. Denn grundsätzlich geht das sehr einfach. Ein Tutorial.

Dieser Blogpost hat zwei Gründe: Zum einen ist es natürlich super, mal zu zeigen, wie Scraping mit Python funktioniert. Ich nutze das gerne, weil ich a) die Sprache super intuitiv und schnell finde und sie b) super simpel zu benutzen ist – auch von einem Webserver aus. Und zum Zweiten habe ich dann einen Anlaufpunkt, um nachzuschauen, wie ich das immer angehe. Wenn ich es doch mal wieder ein paar Wochen nicht gemacht habe und meine Skripte mal wieder suche.

Ich werde in diesem Scraper keine echten Links angeben, nur eine Dummywebseite. Webscraping ist nämlich so eine Sache. Rechtlich gesehen: Eine Webseite kann in ihren Nutzungsbedingungen Scraping verbieten. Nur: Wenn ich mich nicht anmelden muss, akzeptiere ich in der Regel diese Nutzungsbedingungen nicht. Weil ich dazu gar keine Chance habe.

Dann sollte man trotzdem wissen was man tut. Und zwar aus Fairness: Im blödesten Fall kann ich mit einem Scraper nämlich einen Webserver in die Knie zwingen, ähnlich wie bei einer DOS-Attacke (Denial of Service). Indem ich einen Server nämlich mit vielen Anfragen bombardiere. Umgehen kann ich das zum Beispiel dadurch, dass ich die Anfragen zeitlich dosiert rausschicke. Manche Seiten bauen auch explizit Maßnahmen gegen Scraper ein (zum Beispiel Facebook). Auch dann sollte ich (oder gegebenenfalls ein Jurist) abwägen, ob mein Scraping noch legal ist. Aus journalistischer Sicht kann da viel im Namen des Allgemeininteresses machbar sein.

Die Daten, die ich bekomme, kann ich dann in jedem beliebigen Format speichern. Will ich sie hinterher auswerten, dann beispielsweise als CSV (also Semikolon- oder Komma-separiert) oder etwas fancier als JSON- oder XML-Dateien, die bei Enticklern beliebter sind, weil sie komplexere Unterteilungen zulassen, als so eine flache Exceltabelle (oder CSV).

Wie sieht der Scraper also nun aus?

In Python nutze ich dafür zwei Bibliotheken: Requests, um eine Anfrage an einen Server/eine Webseite zu stellen. Und Beautiful Soup, um diese Webseite in ihre Einzelteile (bzw. HTML-Knoten) zu zerlegen und auszulesen. (Es gäbe auch noch die Bibliothek Scrapy, die so ähnlich funktioniert. Und für ganz verrückte Webseiten auch noch Geschichten wie Selenium, die einen ganzen Webbrowser (inklusive Nutzer) vortäuschen können.) Dieses Bibliotheken müssen wir vorher installieren. Zum Beispiel mit pip install.

Dann starten wir in Python, und schrieben unser Skript mit einem einfachen Verbindungsversuch.

Wir können dann das das Objekt r abfragen.

Und genau an dieses HTML wollen wir ran. Je nachdem, welchen Teil der Webseite wir abgreifen wollen, müssen wir einen Weg finden, diesen Teil zu identifizieren. Dass kann über HTML-Tags passieren (alle Links zum Beispiel mit dem a-Tag), oder über CSS-Selektoren (zum Beispiel den Autorennamen eines der letzten Posts über diesen hier: #post-231 > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > section:nth-child(1) > p:nth-child(1) > ba:nth-child(2)). Dafür können wir die Entwicklertools unseres Browsers benutzen, und die entsprechende Stelle auswählen – ich nutze aber auch ganz gerne das Selector Gadget. Ein kleines Addon. Das ist sehr benutzerfreundlich, finde ich. (Wirkt nicht so, wenn man die altbackene Webseite anschaut)

Inhalte auslesen mit Beautiful Soup

Mit der Bibliothek Beautiful Soup kann ich dann mein HTML in ein Soup-Objekt umwandeln. Damit kann ich dann auf die Inhalte meiner einzelnen HTML-Knoten zugreifen. Inhalte können Text sein, aber auch Attribute, wie Link-Ziele.

Und dann gehts weiter. BeautifulSoup hat ein paar Funktionen, die mir wichtige Elemente sehr leicht ausgeben. Zum Beispiel:

Zur Suche hat BeautifulSoup zwei Funktionen: find gibt das erste Ergebnis zurück, find_all alle als Liste.

Ein Beispiel: Example.com

Ein netter Mensch hat die Seite Example.com ins Internet gestellt. Die können wir zu Testzwecken scrapen. Ein Blick in die Entwicklertools zeigt: Die Seite hat eine h1-Überschrift, zwei p-Absätze, wobei der zweite einen Link enthält.

Und genau das wollen wir alles haben. Wir rufen also die Seite auf, wandeln sie in ein BS4-Objekt um, und suchen dann nach h1, p, und dem Linkziel für a.

Damit ich mich noch ein bisschen an Standards halte, füge ich vor mein Skript noch zwei Zeilen ein. Die erste gibt an, wo mein Python3 gespeichert ist (ich könnte ja auch Python2 benutzen, dann würden Teile der Syntax nicht stimmen und ich bekäme einen Fehler). Die zweite Zeile gibt an, in welcher Kodierung ich arbeite. UTF-8 ist am einfachsten, bevor ich Probleme bekomme, wenn das Skript auf Windows- und Mac-Rechnern bearbeitet wurde.

Zack, feddich. So geht ein einfacher Scraper.