Bereits Kunde? Jetzt einloggen.
Lesezeit ca. 6 Min.

Scraping von Webseiten trotz Reactive Design und dynamischer Inhalte: Blick unter die Haube


Linux Magazin - epaper ⋅ Ausgabe 3/2020 vom 06.02.2020

Screen-Scraper scheitern mit ihrer Aufgabe oft an komplexen Webseiten. Mike Schilli steuert deshalb den Chrome-Browser mittels des DevTools-Protokolls fern und entlockt mit dieser Methode selbst hochgradig dynamischen Webseiten ihre Daten. Mike Schilli


Vorbei sind die Zeiten, in denen Hobbyisten einfach schnell mit Curl und Konsorten Webseiten herunterladen konnten, um deren Inhalt maschinell weiterzuverarbeiten. Moderne Webauftritte wimmeln nur so von Reactive Design und dynamischen Inhalten, die nur zum Vorschein kommen, wenn ein echter Webbrowser mit eingeschaltetem Javascript darauf zeigt. ...

Artikelbild für den Artikel "Scraping von Webseiten trotz Reactive Design und dynamischer Inhalte: Blick unter die Haube" aus der Ausgabe 3/2020 von Linux Magazin. Dieses epaper sofort kaufen oder online lesen mit der Zeitschriften-Flatrate United Kiosk NEWS.

Bildquelle: Linux Magazin, Ausgabe 3/2020

Weiterlesen
epaper-Einzelheft 5,99€
NEWS 14 Tage gratis testen
Bereits gekauft?Anmelden & Lesen
Leseprobe: Abdruck mit freundlicher Genehmigung von Linux Magazin. Alle Rechte vorbehalten.

Mehr aus dieser Ausgabe

Titelbild der Ausgabe 3/2020 von Für und Wider. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Für und Wider
Titelbild der Ausgabe 3/2020 von News. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
News
Titelbild der Ausgabe 3/2020 von Zahlen & Trends. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Zahlen & Trends
Titelbild der Ausgabe 3/2020 von Weshalb Edge Computing nötig und wie es realisierbar ist: Rechnen im Außendienst. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Weshalb Edge Computing nötig und wie es realisierbar ist: Rechnen im Außendienst
Titelbild der Ausgabe 3/2020 von Edge in der Operational Technology: Leicht hinterher. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Edge in der Operational Technology: Leicht hinterher
Titelbild der Ausgabe 3/2020 von Interview mit „State-of-the-Edge“-Herausgeber Matt Trifiro: „Edge ist ein Ort“. Zeitschriften als Abo oder epaper bei United Kiosk online kaufen.
Interview mit „State-of-the-Edge“-Herausgeber Matt Trifiro: „Edge ist ein Ort“
Vorheriger Artikel
Simples API mit Go und GraphQL: Hübsch portioniert
aus dieser Ausgabe
Nächster Artikel Vorschau 04/2020
aus dieser Ausgabe

Vorbei sind die Zeiten, in denen Hobbyisten einfach schnell mit Curl und Konsorten Webseiten herunterladen konnten, um deren Inhalt maschinell weiterzuverarbeiten. Moderne Webauftritte wimmeln nur so von Reactive Design und dynamischen Inhalten, die nur zum Vorschein kommen, wenn ein echter Webbrowser mit eingeschaltetem Javascript darauf zeigt.

© Valmedia Creatives, 123RF


Wer etwa einen Screen-Scraper für Gmail schreiben wollte, käme mit einem Scraper-Skript nicht einmal durch die Anmeldung. Auch ein Scraping-Framework wie Colly [1] dringt da nicht durch, da es weder Javascript beherrscht noch eine Vorstellung vom DOM (Document Object Model) des Browsers hat. Die Lösung führt über einen anderen Weg: Das Scraper-Programm dirigiert stattdessen einen echten Browser zur gewünschten Webseite und befragt ihn anschließend, um herauszufinden, was er denn dort so vorfindet.

Der Autor

Michael Schilli arbeitet als Software-Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen verschiedener Programmiersprachen. Unter [mschilli@perlmeister. com] beantwortet er gerne Fragen.

Bei vollautomatischen Unit-Tests für Web- -UIs greifen Entwickler seit Jahren zum Java-Tool Selenium. Dieses kann über das Selenium-Protokoll, das alle Standardbrowser implementieren, den Browser hochfahren. Selenium klickt anschließend auf Links, füllt Formularfelder aus und drückt Submit-Buttons. Googles Chrome- Browser implementiert das DevTools- Protokoll, das Ähnliches leistet, und das Projekt Chromedp auf Github definiert darauf aufbauend eine Go-Bibliothek. Go-Enthusiasten können ihre Unit-Tests und Scraper-Programme nun nativ in ihrer Lieblingssprache schreiben.

Chrome dirigieren

Das Go-Programm in Listing 1 startet zum Beispiel den Chrome-Browser, lotst ihn auf die Seite des Linux-Magazins und erstellt anschließend einen Screenshot des eingeholten Inhalts. Das Ganze läuft von der Kommandozeile mit »go build screenshot.go« ab, gefolgt von »./ screenshot«. Der User sieht dabei auch keinen Browser aufpoppen, da dieser im Normalfall im Headless-Modus läuft, also unsichtbar, falls nichts anderes eingestellt ist. Den Code der Library holt, wie in Go üblich, vorher der folgende Aufruf, der ihn auch gleich übersetzt und installiert:

Nach dem Abholen der Seite, das je nach Internet-Verbindung und Server-Geschwindigkeit einige Sekunden dauert, legt Listing 1 als Ergebnis eine Bilddatei im PNG-Format namens »screenshot.png« auf der Festplatte ab. Da die Homepage des Linux-Magazins sich über mehrere Browser-Längen erstreckt, damit die User auch etwas zum Herunterscrollen finden, ist der erstellte Screenshot in Abbildung 1 fast 7000 Pixel hoch.

Listing 1 erzeugt dazu in Zeile 13 einen neuen Chromedp-Kontext und gibt dem Konstruktor einen in Go üblichen Standard-Background-Kontext mit. Bei Letzterem handelt es sich um ein Hilfskonstrukt zum Steuern von Go-Routinen und Unterprogrammen: Ein Kontext in Go liefert eine »cancel()«-Funktion, die das Hauptprogramm aufrufen kann, um einem anderen Programmteil zu signalisieren, dass es Zeit zum Aufräumen ist.

Die Struktur »Tasks« ab Zeile 17 definiert eine Reihe von Aktionen, die der angeschlossene Chrome-Browser über das DevTools-Protokoll ausführen soll. Der »Navigate«-Task ab Zeile 18 steuert lediglich die Linux-Magazin-Webseite an. Der zweite Task ab Zeile 20 entsteht über die Funktion »ActionFunc()«, mit der sich in Chromedp neue maßgeschneiderte Tasks strukturieren lassen. Im vorliegenden Fall erzeugt der Task mittels »CaptureScreenshot()« in Zeile 33 einen Screenshot der im ferngesteuerten Browser angezeigten Webseite.

Gewaltige Ausmaße

Nun stellt sich die Frage, wie weit es den virtuellen Browser aufzuziehen gilt, denn diese Einstellung bestimmt, was man auf dem Screenshot sieht. Ist nur ein Bruchteil der Webseite zu sehen oder alles, inklusive der nur durch Scrollen erreichbaren Teile? Im vorliegenden Fall soll der Screenshot alles erfassen, was der User sähe, besäße er einen unendlich hohen Bildschirm mit voll aufgezogenem Browser.

Dazu erfasst die Funktion »GetLayout- Metrics()« die Dimensionen des Layouts der dargestellten Seite, und die in Zeile 31 aufgerufene und ab Zeile 60 definierte Funktion »viewPortFix()« stellt die Abmessungen des unsichtbaren Browsers mittels »SetDeviceMetricsOverride()« darauf ein. Den Bildpuffer »buf«, den die Screenshot-Funktion in Zeile 33 zurückgibt, schreibt »WriteFile()« aus der Go-Library Ioutil in eine Bilddatei im PNG-Format auf die Festplatte. Den Ablauf der Tasks steuert die Funktion »Run()« ab Zeile 48.

Die Technik, Screenshots von automatisch eingeholten Webseiten zu erstellen, eröffnet eine Reihe von ungeahnten Möglichkeiten beim Testen neu entwickelter Web-UIs. So kann eine Bilderkennung später feststellen, ob sich die verschiedenen grafischen Elemente des Auftritts auch am richtigen Platz befinden, ohne dass menschliches Testpersonal sich bei jedem Release tatsächlich aufwendig durch den Flow klicken müsste. Auch ließe sich so ein schönes System zum Archivieren von Webauftritten implementieren; im nächsten Jahrhundert würden sich Historiker sicher über die anno 2020 auf der Linux-Magazin-Homepage geschaltete Werbung amüsieren.

Abbildung 1


Einfaches kompliziert

Zu Testzwecken wäre es manchmal ganz nützlich, den ferngesteuerten Browser nicht versteckt im Hintergrund zu starten, sondern sichtbar im Vordergrund. Paradoxerweise gestaltet sich das aber seit Einführung des voreingestellten Hintergrundmodus in Chromedp vor einiger Zeit relativ kompliziert, denn »NewContext()« zum Erzeugen eines neuen Browser-Kontexts konfiguriert die Browser- Einstellungen tief unten im Motorraum der Library und damit von außen unzugänglich.

Listing 1: »screenshot.go«


Listing 2 legt deshalb mit »NewExecAllocator() « einen neuen Browser-Controller an und gibt ihm die Option »NoFirstRun« mit, damit der Browser im Vordergrund läuft. Zurück kommt ein Kontext, der allerdings nicht kompatibel mit dem Kontext-Objekt ist, das Chromedp ver wendet, und in Zeile 24 der ausführenden Funktion »Run()« mitgibt. Den kompatiblen Kontext erzeugt »NewContext()« in Zeile 12, das den vorher erstellten Exec- Kontext als Mutterkontext mitbekommt. Auch der neue Chromedp-Kontext verfügt über eine »cancel()«-Funktion, und die »defer«-Anweisungen in den Zeilen 13 und 14 triggern beide am Ende des Programms, um den aufgespannten Browser sauber zusammenzufalten.

Abbildung 2: Das Abfragefeld der Github-Volltextsuche über alle Repositories hört auf den Namen »q«


Zu Testzwecken fährt Listing 2 lediglich die Homepages des deutschen und des englischen Linux- Magazins an, wartet dann fünf Sekunden mit »Sleep()« und terminiert sich anschließend selbst.

Volltextsuche starten

Wie kann nun die Browser-Drohne Benutzerinteraktionen wie Mausklicks oder Tastatureingaben simulieren, um komplizierteren Webflows zu folgen? Die Library Chromedp bietet dazu Funktionen an, um bestimmte Formfelder oder Buttons aus dem DOM der dargestellten Webseite per XPath-Query auszuwählen und mittels »SendKeys()«, »Submit()« oder »Click()« anzusprechen.

Um zum Beispiel eine Volltextsuche über alle Repositories auf Github abzufeuern, gilt es zunächst zu analysieren, wie denn das Suchfeld dort überhaupt heißt. Ein Blick auf die Entwickleransicht des Chrome-Browsers über das Menü Inspect Elements offenbart, dass das Suchfeld auf das »name«-Attribut »q« hört (Abbildung 2). Zeile 15 aus Listing 3 definiert die zugehörige X-Path-Query »//input[@name="q"]« in der Variablen »sel«, und der Task »WaitVisible()« in Zeile 19 wartet nach dem Anfahren der Github-Seite, bis das Suchfeld übers Netz eintrudelt. Die Funktion »SendKeys()« in Zeile 20 schickt dann den Suchstring (»waaah«) an das Feld und schließt ihn mit einem Newline-Zeichen ab. Das genügt der Github-UI, um die Suche einzuleiten, ohne dass ein Submit-Button zu drücken wäre. Zeile 21 wartet anschließend mit »WaitReady("body", cdp.ByQuery)«, bis das Ergebnis vorliegt, und leitet dann mit einer benutzerdefinierten »ActionFunc()« die Ausgabe des dargestellten HTML-Salats ein.

Listing 2: »foreground.go«


Listing 3: »github.go«


In der Darstellung in Abbildung 3 holt es dann mit »dom.GetDocument()« den Root-Node des HTML-Dokuments und mit »dom.GetOuterHTML()« den zugehörigen HTML-Code ein. Zu Testzwecken gibt die Funktion »Printf()« den Quellcode der dargestellten Seite aus; eine Scraper-Applikation könnte daraus interessante Inhalte extrahieren und weiterverarbeiten.

Nur bedingt geduldet

Chromedp unterstützt zwar alle Chromeund Edge-Browser, nicht aber Firefox, der auf einer anderen Basistechnologie fußt. Das Projekt Chromedp auf Github hadert jedoch offenbar mit neuen Chrome-Versionen und den ständigen Änderungen am DevTools-Protokoll: Manchmal suchen die Selektoren einfach vergebens nach den Feldern der Webseite.

Abbildung 3: Der für unsere Zwecke kopflose Chrome-Browser gibt auf Github.com eine Such-Query ein.


Auf Github stehen offene Issues zum Thema, und die Entwickler versuchen, die Probleme mit neuen Versionen der Bibliothek zu umgehen. Neben Googles offizieller Dokumentation [2] finden Interessierte online zwar einige Tutorials, jedoch keine gründliche und praktische Aufarbeitung des Themas. Ein O’Reilly- Buch [3] enthält viel unnützes Füllmaterial, reißt aber unterschiedliche Themen im Bereich Web-Scraping an und geht auch auf Selenium und Chrome DevTools kurz ein. Populäre Webseiten wie Facebook, Twitter oder Google scheinen ebenfalls daran interessiert zu sein, Scraping mit Tools wie Chromedp möglichst zu erschweren. So verwendet Gmail auf der Login-Seite dynamisch generierte Zufallsnamen für die Eingabefelder. Zudem erfordern Änderungen am Layout oft eine Anpassung des Scrapers, sodass das ganze Unterfangen ein Kopf-an-Kopf-Rennen bleibt. Schließlich wollen die Branchenriesen ja ihre User an die offiziellen Seiten binden, um sie mit Werbung zu bombardieren, denn irgendjemand muss ja am Ende die Stromrechnung bezahlen.

Infos

[1] Scraper Colly: Mike Schilli, „Daten abstauben“, LM 04/ 2019, S. 98, [https:// www. linux‑magazin. de/ 42260]

2] Chrome DevTools: [https:// developers. google. com/ web/ tools/ chrome‑devtools/]

[3] Go Web Scraping Quick Start Guide: [https:// learning. oreilly. com/ library/ view/ go‑web‑scraping/ 9781789615708/ cover. xhtml]

[4] Listings zu diesem Artikel: [http:// www. linux‑magazin. de/ static/ listings/ magazin/ 2020/ 03/ snapshot/]