Zurück zum Blog
Anleitungen
Mihnea-Octavian ManolacheLast updated on Mar 31, 20269 min read

Der ultimative Leitfaden zum Erstellen eines Web-Scrapers mit Pyppeteer

Der ultimative Leitfaden zum Erstellen eines Web-Scrapers mit Pyppeteer

Wenn es um Python und Web-Automatisierung ging, war Selenium so gut wie die erste Wahl. Zumindest bis jetzt. Aufgrund des Erfolgs von Puppeteer in der JavaScript-Community begannen Python-Entwickler, sich immer intensiver damit zu beschäftigen. Und so entstand Pyppeteer. Aber was genau ist Pyppeteer? Und warum sollten wir es Selenium vorziehen? Ist es zuverlässig genug, um damit eine komplexe Lösung zu entwickeln? All diese Fragen und viele mehr werden wir im heutigen Artikel beantworten. Mein Ziel für heute ist es, dass Sie nach der Lektüre dieses Artikels zumindest Folgendes mitnehmen:

  • Eine Definition von Pyppeteer und seine Anwendungsfälle
  • Ein Verständnis dafür, wie sich Pyppeteer im Vergleich zu Selenium verhält
  • Eine konkrete Umsetzung eines Web-Scrapers mit Pyppeteer

Machen Sie sich also bereit, denn heute werden wir darüber sprechen und selbst Hand anlegen!

Was ist Pyppeteer eigentlich und wie kann man es nutzen?

Wenn Sie dies lesen, sind Sie wahrscheinlich bereits mit Web-Scripting im Allgemeinen vertraut. Und je nach Ihrer bevorzugten Programmiersprache haben Sie wahrscheinlich schon von Puppeteer oder Selenium gehört. Pyppeteer ist jedoch tatsächlich ein Neuling in der Welt des Web-Scrapings. Kurz gesagt: Pyppeteer ähnelt Puppeteer viel mehr als Selenium.

Puppeteer ist eine Node.js-Bibliothek, die die Steuerung einer Headless-Version von Chrome über das DevTools-Protokoll ermöglicht. Pyppeteer ist eine Python-Portierung von Puppeteer. Genau wie das ursprüngliche Puppeteer ist Pyppeteer eine in Python geschriebene Bibliothek, die im Grunde einen Browser automatisiert. Mit anderen Worten: Pyppeteer ist eine Python-Implementierung der Puppeteer-API, die es Ihnen ermöglicht, die Funktionen von Puppeteer in einer Python-Umgebung zu nutzen. Der Hauptunterschied zwischen den beiden ist die verwendete Sprache.

Pyppeteer-Begriffe, die Sie kennen sollten

Bevor wir fortfahren, sollten wir einige Begriffe besprechen, die im Zusammenhang mit Pyppeteer häufig verwendet werden:

  • Headless: Das bedeutet, einen Browser ohne grafische Benutzeroberfläche (GUI) zu starten. Mit anderen Worten: Er läuft „im Hintergrund“ und ist auf dem Bildschirm nicht zu sehen. Dies wird in der Regel verwendet, um den Ressourcenverbrauch beim Scraping zu reduzieren.
  • Headful: Umgekehrt ist ein „Headful“-Browser ein Browser, der mit einer GUI läuft. Dies ist das Gegenteil eines Headless-Browsers und wird oft zum Testen, Debuggen oder zur manuellen Interaktion mit Webseiten verwendet.
  • Browser-Kontext: Dies ist ein Zustand, der von allen Seiten in einem Browser gemeinsam genutzt wird. Er wird in der Regel verwendet, um browserweite Einstellungen wie Cookies, HTTP-Header und Geolokalisierung festzulegen.
  • DOM: Das Document Object Model (DOM) ist eine Programmierschnittstelle für HTML- und XML-Dokumente. Es stellt die Struktur einer Webseite in einem baumartigen Format dar, wobei Knoten Elemente repräsentieren. Mit Pyppeteer können Sie durch Manipulation des DOM mit den Elementen einer Seite interagieren.
  • Elemente: Die Bausteine einer Webseite. Sie werden mithilfe von Tags, Attributen und Werten definiert.

Natürlich steckt noch mehr dahinter, und Sie werden im Laufe der Zeit noch mehr lernen. Aber ich wollte, dass Sie einen Überblick bekommen, damit wir einen soliden Start haben. Ich bin überzeugt, dass die Kenntnis dieser Begriffe Ihnen helfen wird, den Kern dieses Artikels besser zu verstehen.

Warum sollten Sie Pyppeteer in Ihrem Scraping-Projekt verwenden?

Ich denke, es gibt zwei Aspekte in dieser Frage. Der erste ist, warum Pyppeteer generell eine gute Wahl für das Web-Scraping ist. Der zweite ist, warum man Pyppeteer gegenüber Selenium bevorzugen sollte. Im Allgemeinen sind einige der Vorteile von Pyppeteer:

  • Auswerten von JavaScript: Pyppeteer bietet eine `page.evaluate()`-Funktion. Damit können Sie JavaScript-Code im Kontext der Seite ausführen.
  • Netzwerksteuerung: Pyppeteer bietet die Methode `page.on()`. Damit können Sie auf Netzwerkereignisse wie Anfragen und Antworten achten, die auf einer Seite stattfinden.
  • Verfolgung und Protokollierung: Mit Pyppeteer können Sie die Aktivitäten des Browsers verfolgen und Browser-Meldungen von einer Seite protokollieren. Dies erleichtert das Debuggen, die Verfolgung und das Verständnis der Funktionsweise einer Website.

Im Vergleich zu Selenium ist es recht ähnlich, da beide zur Automatisierung eines Webbrowsers verwendet werden. Es gibt jedoch einige wesentliche Unterschiede und Vorteile, die Pyppeteer gegenüber Selenium bietet:

  • Einfachheit: Pyppeteer verfügt über eine einfachere und konsistentere API als Selenium, was die Nutzung für Anfänger erleichtert. Die Pyppeteer-API basiert auf dem DevTools-Protokoll, das eng mit dem Browser verzahnt ist und leicht zu erlernen und zu verwenden ist.
  • Leistung: Pyppeteer kann schneller sein als Selenium, da es auf dem DevTools-Protokoll aufbaut. Das Protokoll ist für das Debuggen von Webseiten konzipiert und deutlich schneller als Selenium WebDriver.
  • Bessere Netzwerksteuerung: Pyppeteer ermöglicht eine bessere Kontrolle über die Netzwerkeinstellungen des Browsers, wie z. B. das Abfangen von Anfragen und das Blockieren von Anfragen/Antworten. Dies erleichtert das Testen und Diagnostizieren von netzwerkbezogenen Problemen.

Und natürlich ist es auch eine Frage der Wahl. Nehmen wir mich zum Beispiel. Im Alltag programmiere ich in JavaScript. Und ich bin ziemlich vertraut mit Pyppeteer. Meine Lieblingsprogrammiersprache ist jedoch Python. Wenn ich also einen Scraper mit einer bekannten Technologie in einer Sprache erstellen würde, die ich bevorzuge, würde ich mich wahrscheinlich für Pyppeteer entscheiden.

Und damit haben wir, glaube ich, die „theoretischen“ Aspekte dieses Artikels abgedeckt. Es ist Zeit, mit dem eigentlichen Programmieren zu beginnen.

So erstellst du einen Web-Scraper mit Pyppeteer

Bevor wir mit dem Programmieren beginnen, möchte ich euch die offizielle Pyppeteer-Dokumentation vorstellen. Ich bin ein Befürworter davon, die offizielle Dokumentation zu nutzen, wann immer man nicht weiterkommt. Das gilt, bevor man Fragen in der Community (wie auf Stackoverflow) stellt. Ich stelle meist fest, dass man die meisten Antworten findet, wenn man einfach zuerst die Dokumentation liest. Betrachten Sie dies also als eine freundliche Bitte von mir. Wann immer Sie nicht weiterkommen, schauen Sie in der Dokumentation nach, suchen Sie dann nach Antworten und stellen Sie Fragen nur als letzten Ausweg.

#1: Die Umgebung einrichten

Das Wichtigste zuerst: Als Python-Entwickler sind Sie wahrscheinlich mit virtuellen Umgebungen vertraut. Als Erstes müssen wir also eine virtuelle Umgebung für unser Projekt erstellen. Im Allgemeinen verwende ich folgende Befehlsfolge:

# Create a new directory and navigate into it

~ » mkdir py_project && cd py_project

# Create the virtual environment

~ » python3 -m venv env

# Activate the virtual environment

~ » source env/bin/activate


Was die virtuelle Umgebung angeht, bist du nun startklar. Es ist Zeit, weiterzumachen und Pyppeteer zu installieren. Da du dein Terminal geöffnet hast, gib einfach Folgendes ein:

# Install the package using pip

~ » python3 -m pip install pyppeteer

# Open the project in your IDE

~ » code .

#2: Erstellen eines einfachen Pyppeteer-Scrapers

Der letzte Befehl öffnet Visual Studio Code oder Ihre bevorzugte IDE. Da Sie sich nun in der „Entwicklungsumgebung“ befinden, erstellen wir eine neue `.py`-Datei, die unseren Code enthalten wird. Ich nenne meine Datei `scraper.py`. Beachten Sie, dass Pyppeteer von Haus aus asynchrone Ausführung unterstützt. Importieren wir also sowohl `asyncio` als auch `pyppeteer` in unsere Datei:

import asyncio
from pyppeteer import launch

Nachdem dies erledigt ist, können wir zu komplexerem Code übergehen. Generell bin ich kein großer Befürworter der funktionalen Programmierung. Dennoch glaube ich, dass das Aufteilen des Codes in kleine Abschnitte das Lernen erleichtert. Packen wir unseren Code also in eine Funktion:

async def scrape(url):

   browser = await launch()

   page = await browser.newPage()

   await page.goto(url)

   content = await page.content()

   await browser.close()

  

   return content

Diese Funktion nimmt eine URL als Eingabe entgegen und startet mithilfe von Pyppeteer einen Headless-Browser. Anschließend navigiert sie zu der angegebenen URL, ruft den Inhalt der Seite ab und schließt den Browser. Der zurückgegebene Wert ist nichts anderes als der von der Seite gesammelte HTML-Code. Mit dieser Funktion können Sie fast jede Website scrapen. Um die Funktion zu nutzen, würden Sie sie in einer `asyncio`-Ereignisschleife wie folgt aufrufen:

async def main():

   content = await scrape('https://www.example.com')

   print(content)

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

#3: Weitere Funktionen hinzufügen

Bis hierhin haben wir einen funktionierenden Scraper. Aber das ist so ziemlich alles, was wir haben. Wenn du einen fortgeschritteneren Web-Scraper mit Pyppeteer erstellen möchtest, musst du id um weitere Funktionen erweitern. Spoiler-Alarm: Wir tauchen in die Welt der objektorientierten Programmierung ein. Aber zuerst wollen wir unsere Ziele festlegen. Was soll unser Scraper können?

  • Den Browser mit einigen benutzerdefinierten Werten initialisieren
  • Auf einer Webseite navigieren und Inhalte extrahieren
  • Text in ein Eingabefeld schreiben
  • Den Wert eines einzelnen Elements extrahieren
  • Werte aus mehreren Elementen extrahieren

3.1. Benutzerdefinierte Optionen

Erstellen wir also zunächst eine neue `Scraper`-Klasse und fügen wir ihre Methoden später hinzu:

class Scraper:

   def __init__(self, launch_options: dict) -> None:

   	self.options = launch_options['options']

 	self.viewPort = launch_options['viewPort'] if 'viewPort' in launch_options else None

pass

Das einzige Argument, das wir für unseren Scraper verwenden, ist ein `launch_options`-Wörterbuch. Wie du siehst, enthält es zwei Schlüssel. Ein Schlüssel definiert die Launcher-Optionen von Pyppeteer. Die zweite Option ist entweder `None` oder ein Wörterbuch, das die `width` und `height` des `viewPort` enthält. Letzteres wird für diese Methode verwendet.

3.2. Zu einer Seite navigieren

Wenn Sie sich die zuvor verwendete Funktion ansehen, werden Sie feststellen, dass wir sowohl die Navigation als auch das Extrahieren von Rohdaten aus einer bestimmten URL abdecken. Wir müssen die Funktion lediglich anpassen und in eine Methode für unseren Scraper umwandeln:

async def goto(self, url: str) -> None:

       self.browser = await launch(options=self.options)

       self.page = await self.browser.newPage()

       await self.page.setViewport(self.viewPort) if self.viewPort != None else print('[i] Using default viewport')

       await self.page.goto(url)

Diese Methode ist recht einfach. Zunächst startet sie einen neuen Browser mit den zuvor festgelegten benutzerdefinierten Optionen. Anschließend erstellt sie eine neue Seite und legt, sofern unser `launch_options`-Wörterbuch einen `viewPort` enthält, den ViewPort der Seite fest. Andernfalls protokolliert sie eine einfache Meldung. Zu guter Letzt führt sie uns zum Ziel.

3.3. Rohdaten aus einer Seite extrahieren

Auch hier haben wir die Methode in unserer ursprünglichen `scraper`-Funktion. Wir warten lediglich darauf, dass `page.content()` geladen wird, und geben dessen Wert zurück:

async def get_full_content(self) -> str:

       content = await self.page.content()

       return content

3.4. Text in ein Eingabefeld schreiben

Um mit Pyppeteer etwas in ein Eingabefeld zu schreiben, benötigen Sie zwei Dinge. Erstens: das Element lokalisieren. Zweitens: einen Wert hinzufügen. Glücklicherweise verfügt Pyppeteer über Methoden für beide Aktionen:

async def type_value(self, selector: str, value: str) -> None:

       element = await self.page.querySelector(selector)

       await element.type(value)

3.5. Wert aus einer Seite extrahieren

Denken Sie daran, dass wir entweder den Wert aus einem einzelnen Element oder Werte aus mehreren Elementen extrahieren wollen. Wir könnten für beides eine einzige Methode verwenden. Aber ich mag es normalerweise, wenn Dinge getrennt sind. Deshalb füge ich vorerst zwei weitere Methoden hinzu:

async def extract_one(self, selector) -> str:

       element = await self.page.querySelector(selector)

       text = await element.getProperty("textContent")

       return await text.jsonValue()

Hier suchen wir das Element mithilfe der Methode `querySelector`. Anschließend warten wir auf den `textContent` und geben dessen `jsonValue()` zurück. Wenn wir hingegen mehrere Elemente auswählen möchten, verwenden wir `querySelector`:

async def extract_many(self, selector) -> list:

       result = []

       elements = await self.page.querySelectorAll(selector)

       for element in elements:

           text = await element.getProperty("textContent")

           result.append(await text.jsonValue())

       return result

Diese Methode funktioniert ähnlich wie `extract_one`. Der einzige Unterschied ist der Rückgabewert. Diesmal geben wir eine Liste aller Texte innerhalb der ausgewählten Elemente zurück. Und ich denke, mit dieser Ergänzung haben wir alle unsere Ziele erreicht.

#4: Mach es unauffällig

Beim Web Scraping kann „Unauffälligkeit“ als die Fähigkeit beschrieben werden, unentdeckt zu bleiben. Natürlich erfordert die Entwicklung eines vollständig unentdeckbaren Scrapers viel Arbeit. Der Stealth-Modus der Web Scraping API wird beispielsweise von einem eigenen Team gepflegt. Und der Aufwand, der darin steckt, macht den Fingerabdruck unseres Scrapers bei jeder Anfrage einzigartig.

Mein übergeordnetes Ziel für dieses Tutorial ist es jedoch, euch auf den richtigen Weg zu bringen. Und der richtige Weg zu einem vollständigen Web-Scraper mit Pyppeteer beinhaltet, ihm einige Stealth-Funktionen hinzuzufügen. Glücklicherweise gibt es, genau wie es in Node das `puppeteer-extra-plugin-stealth` gibt, auch ein Paket für Python. Und es heißt, ganz intuitiv, `pyppeteer-stealth`. Um es zu Ihrem Projekt hinzuzufügen, installieren Sie es zunächst mit pip:

~ » python3 -m pip install pyppeteer_stealth

Importiere es dann in dein Projekt und füge einfach eine zusätzliche Codezeile hinzu:

async def goto(self, url: str) -> None:

       self.browser = await launch(options=self.options)

       self.page = await self.browser.newPage()

	  # Make it stealthy

       await stealth(self.page)  

       await self.page.setViewport(self.viewPort) if self.viewPort != None else print('[i] Using default viewport')

       await self.page.goto(url)

Und so führen Sie Ihren Scraper aus. Ich habe dem Code einige Kommentare hinzugefügt, um zu verdeutlichen, was jeder Schritt bewirkt:

async def main():

   # Define the launch options dictionary

   launch_options = {

       'options': {

           'headless': False,

           'autoClose': True

       },

       'viewPort': {

           'width': 1600,

           'height': 900

       }

   }

   # Initialize a new scraper

   scraper = Scraper(launch_options)

   # Navigae to your target

   await scraper.goto('https://russmaxdesign.github.io/accessible-forms/accessible-name-input01.html')

   # Type `This is me` inside the input field

   await scraper.type_value(

       '#fish',

       'This is me')

   # Scrape the entire page

   content = await scraper.get_full_content()

   print(content)

   # Scrape one single element

   el = await scraper.extract_one('body > div:nth-child(14) > ul')

   print(el)

   # Scrape multiple elements

   els = await scraper.extract_many('p')

   print(els)

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

Fazit

Pyppeteer ist ein großartiges Tool für das Web-Scraping. Es portiert die gesamte Puppeteer-API nach Python und ermöglicht es der Python-Community, diese Technologie zu nutzen, ohne JavaScript lernen zu müssen. Außerdem glaube ich nicht, dass es ein Ersatz für Selenium ist, aber es ist sicherlich eine gute Alternative dazu.

Ich hoffe, der heutige Artikel hat deinen Lernprozess bereichert. Und da ich gerne die Grenzen aller verschiebe, fordere ich dich heraus, das heute Gelernte noch zu erweitern. Der Scraper, den wir gemeinsam erstellt haben, ist ein wirklich guter Ausgangspunkt und führt ein Schlüsselelement der Programmierung ein: OOP. Also fordere ich dich heraus, weitere Methoden zu `Scraper` hinzuzufügen und ihn wirklich großartig zu machen.

Über den Autor
Mihnea-Octavian Manolache, Full-Stack-Entwickler @ WebScrapingAPI
Mihnea-Octavian ManolacheFull-Stack-Entwickler

Mihnea-Octavian Manolache ist Full-Stack- und DevOps-Entwickler bei WebScrapingAPI. Er entwickelt Produktfunktionen und sorgt für die Wartung der Infrastruktur, die den reibungslosen Betrieb der Plattform gewährleistet.

Los geht’s

Sind Sie bereit, Ihre Datenerfassung zu erweitern?

Schließen Sie sich den über 2.000 Unternehmen an, die WebScrapingAPI nutzen, um Webdaten im Unternehmensmaßstab ohne zusätzlichen Infrastrukturaufwand zu extrahieren.