Zurück zum Blog
Anleitungen
Gabriel CiociLast updated on Mar 31, 20266 min read

Web-Scraping mit Puppeteer – Fortgeschrittene Node.JS

Web-Scraping mit Puppeteer – Fortgeschrittene Node.JS

Anstatt kommerzielle Tools zu nutzen, ziehen es viele Entwickler vor, ihre eigenen Web-Scraper zu erstellen. Zwar bieten die verfügbaren Produkte umfangreichere Funktionen, doch lassen sich weder die Ergebnisse, die diese Bots liefern, noch der Spaß am Eigenbau leugnen.

Im folgenden Artikel erfährst du, welche Schritte du unternehmen musst, um deinen eigenen Web-Scraper mit Node.js und Puppeteer zu erstellen. Wir programmieren eine App, die eine Website lädt, einen Screenshot macht, sich über einen Headless-Browser bei der Website anmeldet und Daten über mehrere Seiten hinweg scrapt. Deine App wird im Laufe des Prozesses immer komplexer.

Ein Überblick über Web-Scraping mit Puppeteer

Ein Überblick über Web-Scraping mit Puppeteer

Google hat Puppeteer entwickelt, um in Node.js eine einfache, aber leistungsstarke Schnittstelle für die Automatisierung von Tests und verschiedenen Aufgaben unter Verwendung der Chromium-Browser-Engine bereitzustellen. Es läuft standardmäßig headless, kann aber so konfiguriert werden, dass es Chrome oder Chromium in voller Funktion ausführt.

Die vom Puppeteer-Team entwickelte API nutzt das DevTools-Protokoll, um die Kontrolle über einen Webbrowser wie Chrome zu übernehmen und verschiedene Aufgaben auszuführen, wie zum Beispiel:

  • Screenshots aufnehmen und PDFs von Seiten erstellen
  • Automatisieren der Formularübermittlung
  • UI-Tests (Klicken auf Schaltflächen, Tastatureingaben usw.)
  • Ein SPA scrapen und vorgerenderte Inhalte generieren (Server-Side Rendering)

Die meisten Aktionen, die Sie manuell im Browser ausführen können, lassen sich auch mit Puppeteer durchführen. Darüber hinaus können sie automatisiert werden, sodass Sie Zeit sparen und sich auf andere Aufgaben konzentrieren können.

Puppeteer wurde zudem entwicklerfreundlich konzipiert. Wer mit anderen beliebten Test-Frameworks wie Mocha vertraut ist, wird sich mit Puppeteer sofort wohlfühlen und eine aktive Community vorfinden, die Unterstützung für Puppeteer bietet. Dies führte zu einem massiven Anstieg der Beliebtheit unter Entwicklern.

Natürlich eignet sich Puppeteer nicht nur zum Testen. Denn wenn es alles kann, was ein Standardbrowser kann, dann kann es auch für Web-Scraper äußerst nützlich sein. Konkret kann es dabei helfen, JavaScript-Code auszuführen, damit der Scraper auf den HTML-Code der Seite zugreifen kann, und normales Nutzerverhalten nachzuahmen, indem es durch die Seite scrollt oder auf zufällige Abschnitte klickt.

Diese dringend benötigten Funktionen machen Headless-Browser zu einer Kernkomponente für jedes kommerzielle Datenextraktions-Tool und für alle Web-Scraper außer den einfachsten selbst erstellten.

Voraussetzungen

Voraussetzungen

Stellen Sie zuallererst sicher, dass Sie aktuelle Versionen von Node.js und Puppeteer auf Ihrem Rechner installiert haben. Ist dies nicht der Fall, können Sie die folgenden Schritte befolgen, um alle Voraussetzungen zu installieren.

Sie können Node.js hier herunterladen und installieren. Der Standard-Paketmanager von Node, npm, ist bereits in Node.js vorinstalliert.

Um die Puppeteer-Bibliothek zu installieren, können Sie den folgenden Befehl im Stammverzeichnis Ihres Projekts ausführen:

npm install puppeteer
# or "yarn add puppeteer"

Beachten Sie, dass bei der Installation von Puppeteer auch die neueste Version von Chromium heruntergeladen wird, die garantiert mit der API kompatibel ist.

Puppeteer in Aktion

Puppeteer in Aktion

Mit der Bibliothek lassen sich viele verschiedene Dinge realisieren. Da unser Schwerpunkt auf Web-Scraping liegt, werden wir die Anwendungsfälle besprechen, die für Sie am interessantesten sind, wenn Sie Webdaten extrahieren möchten.

Einen Screenshot erstellen

Einen Screenshot erstellen

Beginnen wir mit einem einfachen Beispiel. Wir schreiben ein Skript, das einen Screenshot einer Website unserer Wahl erstellt.

Beachten Sie, dass Puppeteer eine Promise-basierte Bibliothek ist (sie führt im Hintergrund asynchrone Aufrufe an die Headless-Chrome-Instanz durch). Halten wir den Code also übersichtlich, indem wir async/await verwenden.

Erstellen Sie zunächst eine neue Datei namens index.js im Stammverzeichnis Ihres Projekts.

In dieser Datei müssen wir eine asynchrone Funktion definieren und den gesamten Puppeteer-Code darin einbinden.

const puppeteer = require('puppeteer')

async function snapScreenshot() {
	try {
		const URL = 'https://old.reddit.com/'
		const browser = await puppeteer.launch()
		const page = await browser.newPage()

		await page.goto(URL)
		await page.screenshot({ path: 'screenshot.png' })

		await browser.close()
	} catch (error) {
		console.error(error)
	}
}

snapScreenshot()

Zunächst wird mit dem Befehl puppeteer.launch() eine Instanz des Browsers gestartet. Dann erstellen wir mit der Browser-Instanz eine neue Seite. Um zur gewünschten Website zu navigieren, können wir die Methode goto() verwenden und die URL als Parameter übergeben. Um einen Screenshot zu erstellen, verwenden wir die Methode screenshot(). Außerdem müssen wir den Speicherort angeben, an dem das Bild gespeichert werden soll.

Beachte, dass Puppeteer die anfängliche Seitengröße auf 800×600px festlegt, was die Größe des Screenshots bestimmt. Du kannst die Seitengröße mit der Methode setViewport() anpassen.

Vergessen Sie nicht, die Browser-Instanz zu schließen. Dann müssen Sie nur noch node index.js im Terminal ausführen.

Es ist wirklich so einfach! Du solltest nun eine neue Datei namens screenshot.png in deinem Projektordner sehen.

Ein Formular absenden

Ein Formular absenden

Wenn die Website, die du scrapen möchtest, aus irgendeinem Grund den Inhalt erst anzeigt, wenn du angemeldet bist, kannst du den Anmeldevorgang mit Puppeteer automatisieren.

Zunächst müssen wir die Website, die wir scrapen, untersuchen und die Anmeldefelder finden. Dazu klicken wir mit der rechten Maustaste auf das Element und wählen die Option „Inspect“.

In meinem Fall befinden sich die Eingabefelder in einem Formular mit der Klasse „login-form“. Wir können die Anmeldedaten mit der Methode „type()“ eingeben.

Wenn du sicherstellen möchtest, dass die richtigen Aktionen ausgeführt werden, kannst du den Parameter „headless“ hinzufügen und ihn beim Starten der Puppeteer-Instanz auf „false“ setzen. Du wirst dann sehen, wie Puppeteer den gesamten Prozess für dich erledigt.

const puppeteer = require('puppeteer')

async function login() {
   try {
       const URL = 'https://old.reddit.com/'
       const browser = await puppeteer.launch({headless: false})
       const page = await browser.newPage()

       await page.goto(URL)

       await page.type('.login-form input[name="user"]', 'EMAIL@gmail.com')
       await page.type('.login-form input[name="passwd"]', 'PASSWORD')

       await Promise.all([
           page.click('.login-form .submit button'),
           page.waitForNavigation(),
       ]);
      
       await browser.close()

   } catch (error) {
       console.error(error)
   }
}

login()

Um einen Mausklick zu simulieren, können wir die Methode click() verwenden. Nachdem wir auf die Anmeldeschaltfläche geklickt haben, sollten wir warten, bis die Seite geladen ist. Dies können wir mit der Methode waitForNavigation() tun.

Wenn wir die richtigen Anmeldedaten eingegeben haben, sollten wir nun angemeldet sein!

Mehrere Seiten scrapen

Mehrere Seiten scrapen

Ich werde für diesen Artikel das Subreddit /r/learnprogramming verwenden. Wir wollen also zur Website navigieren und den Titel sowie die URL jedes Beitrags abrufen. Dazu verwenden wir die Methode evaluate().

Der Code sollte wie folgt aussehen:

const puppeteer = require('puppeteer')

async function tutorial() {
   try {
       const URL = 'https://old.reddit.com/r/learnprogramming/'
       const browser = await puppeteer.launch()
       const page = await browser.newPage()

       await page.goto(URL)
       let data = await page.evaluate(() => {
           let results = []
           let items = document.querySelectorAll('.thing')
           items.forEach((item) => {
               results.push({
                   url: item.getAttribute('data-url'),
                   title: item.querySelector('.title').innerText,
               })
           })
           return results
       })

       console.log(data)
       await browser.close()

   } catch (error) {
       console.error(error)
   }
}

tutorial()

Mit der zuvor vorgestellten Inspect-Methode können wir alle Beiträge abrufen, indem wir den Selektor .thing anvisieren. Wir durchlaufen sie nacheinander und holen für jeden die URL und den Titel ab, die wir in ein Array einfügen.

Nachdem der gesamte Vorgang abgeschlossen ist, kannst du das Ergebnis in deiner Konsole sehen.

Großartig, wir haben die erste Seite gescrapt. Aber wie scrapen wir mehrere Seiten dieses Subreddits?

Das ist einfacher, als du denkst. Hier ist der Code:

const puppeteer = require('puppeteer')

async function tutorial() {
   try {
       const URL = 'https://old.reddit.com/r/learnprogramming/'
       const browser = await puppeteer.launch({headless: false})
       const page = await browser.newPage()

       await page.goto(URL)
       let pagesToScrape = 5;
       let currentPage = 1;
       let data = []
       while (currentPage <= pagesToScrape) {
           let newResults = await page.evaluate(() => {
               let results = []
               let items = document.querySelectorAll('.thing')
               items.forEach((item) => {
                   results.push({
                       url: item.getAttribute('data-url'),
                       text: item.querySelector('.title').innerText,
                   })
               })
               return results
           })
           data = data.concat(newResults)
           if (currentPage < pagesToScrape) {
               await page.click('.next-button a')
               await page.waitForSelector('.thing')
               await page.waitForSelector('.next-button a')
           }
           currentPage++;
       }
       console.log(data)
       await browser.close()
   } catch (error) {
       console.error(error)
   }
}

tutorial()

Wir benötigen eine Variable, um zu wissen, wie viele Seiten wir scrapen möchten, und eine weitere Variable für die aktuelle Seite. Solange die aktuelle Seite kleiner oder gleich der Anzahl der Seiten ist, die wir scrapen möchten, erfassen wir die URL und den Titel für jeden Beitrag auf der Seite. Nachdem jede Seite erfasst wurde, fügen wir die neuen Ergebnisse mit den bereits gescrapten zusammen.

Dann klicken wir auf die Schaltfläche „Nächste Seite“ und wiederholen den Scraping-Vorgang, bis wir die gewünschte Anzahl an extrahierten Seiten erreicht haben. Außerdem müssen wir die aktuelle Seite nach jeder Seite erhöhen.

Eine noch einfachere Option

Eine noch einfachere Option

Herzlichen Glückwunsch! Sie haben erfolgreich Ihren eigenen Web-Scraper mit Puppeteer erstellt. Ich hoffe, das Tutorial hat Ihnen gefallen!

Dennoch kann das Skript, das wir in dieser Anleitung erstellt haben, keine besonders anspruchsvollen Aufgaben bewältigen. Es fehlen einige wichtige Aspekte, die das Web-Scraping erst wirklich reibungslos machen. Die Verwendung von mobilen oder privaten Proxys und das Lösen von CAPTCHAs sind nur einige der fehlenden Funktionen.

Wenn Sie nach einer professionelleren Methode zur Datenextraktion suchen, schauen Sie sich an, was WebScrapingAPI leisten kann, und prüfen Sie, ob es zu Ihren Anforderungen passt. Es gibt ein kostenloses Paket, Sie müssen also lediglich 30 Minuten Ihrer Zeit investieren.

Viel Spaß beim Web Scraping!

Über den Autor
Gabriel Cioci, Full-Stack-Entwickler @ WebScrapingAPI
Gabriel CiociFull-Stack-Entwickler

Gabriel Cioci ist Full-Stack-Entwickler bei WebScrapingAPI und verantwortlich für die Entwicklung und Wartung der Websites, des Benutzerportals sowie der wichtigsten benutzerseitigen Komponenten der Plattform.

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.