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

So erstellen Sie einen Scraper und laden eine Datei mit Puppeteer herunter

So erstellen Sie einen Scraper und laden eine Datei mit Puppeteer herunter

Wenn du dich für Web-Scraping interessierst und Node.js verwendest, hast du höchstwahrscheinlich schon von Puppeteer gehört. Und du bist bestimmt schon einmal auf eine Aufgabe gestoßen, bei der du mit Puppeteer eine Datei herunterladen musstest. Dies ist in der Scraping-Community tatsächlich eine häufig vorkommende Aufgabe. In der Puppeteer-Dokumentation wird sie jedoch nicht ausführlich behandelt.

Glücklicherweise werden wir uns gemeinsam darum kümmern. In diesem Artikel werden wir das Herunterladen von Dateien in Puppeteer besprechen. Es gibt zwei Ziele, die wir heute ansprechen möchten:

  • Ein solides Verständnis dafür entwickeln, wie Puppeteer mit Downloads umgeht
  • Erstellen eines funktionierenden Scrapers zum Herunterladen von Dateien mit Node und Puppeteer

Am Ende dieses Artikels werden Sie sowohl die theoretischen als auch die praktischen Fähigkeiten erworben haben, die ein Entwickler benötigt, um einen Datei-Scraper zu erstellen. Wenn dieses Projekt für Sie genauso spannend klingt wie für mich, lassen Sie uns loslegen!

Warum Dateien mit Puppeteer herunterladen?

Es gibt viele Anwendungsfälle für einen Datei-Scraper, und StackOverflow ist voll von Entwicklern, die nach Antworten suchen, wie man Dateien mit Puppeteer herunterlädt. Dabei müssen wir bedenken, dass zu Dateien Bilder, PDFs, Excel- oder Word-Dokumente und vieles mehr gehören. Es ist leicht nachvollziehbar, warum all diese Dateien für manche Menschen sehr wichtige Informationen liefern können.

Zum Beispiel gibt es Unternehmen in der Dropshipping-Branche, die auf Bilder angewiesen sind, die aus externen Quellen wie Marktplätzen gescrapt werden. Ein weiteres gutes Beispiel für den Einsatz eines Datei-Scrapers sind Unternehmen, die offizielle Dokumente überwachen. Oder auch kleine Projekte. Ich selbst habe ein Skript, das Rechnungen von der Website eines Partners herunterlädt.

Was nun die Verwendung von Puppeteer zum Herunterladen von Dateien angeht, stelle ich fest, dass sich die meisten Leute vor allem aus zwei Gründen dafür entscheiden:

  • Es ist für Node.js konzipiert, und Node.js ist eine der beliebtesten Programmiersprachen, sowohl für das Frontend als auch für das Backend.
  • Es öffnet einen echten Browser, und einige Websites sind auf JavaScript angewiesen, um Inhalte darzustellen. Das bedeutet, dass man die Dateien nicht mit einem normalen HTTP-Client herunterladen könnte, der keine JavaScript-Dateien rendern kann.

Wie Puppeteer das Herunterladen von Dateien handhabt

Um zu verstehen, wie man mit Puppeteer Dateien herunterlädt, müssen wir wissen, wie Chrome dies tut. Denn im Kern ist Puppeteer eine Bibliothek, die Chrome über das Chrome DevTools Protocol (CDP) „steuert“.

In Chrome können Dateien heruntergeladen werden:

  • Manuell, zum Beispiel durch einen Klick auf eine Schaltfläche
  • Programmgesteuert, über die Page Domain aus dem CDP.

Und es gibt noch eine dritte Technik, die beim Web-Scraping zum Einsatz kommt. Dabei wird nämlich ein neuer Akteur integriert: ein HTTP-Client. Auf diese Weise sammelt der Web-Scraper die `hrefs` der Dateien und anschließend wird ein HTTP-Client verwendet, um die Dateien herunterzuladen. Jede Option hat ihre eigenen Anwendungsfälle, daher werden wir beide Methoden untersuchen.

Dateien in Puppeteer mit einem Klick auf eine Schaltfläche herunterladen

In dem glücklichen Fall, dass die Website, von der Sie Dateien scrapen möchten, Schaltflächen verwendet, müssen Sie lediglich das Klick-Ereignis in Puppeteer simulieren. Die Implementierung eines Datei-Downloaders ist in diesem Szenario ziemlich einfach. Puppeteer dokumentiert sogar die Methode `Page.click()` und weitere Informationen finden Sie hier.

Da es sich hierbei um ein „menschliches“ Verhalten handelt, müssen wir Folgendes tun:

  • Öffnen Sie den Browser
  • Zur Ziel-Webseite navigieren
  • Das Schaltflächenelement lokalisieren (z. B. über seinen CSS-Selektor oder xPath)
  • Auf die Schaltfläche klicken

Es sind nur vier einfache Schritte, die wir in unserem Skript implementieren müssen. Bevor wir uns jedoch an die Programmierung machen, möchte ich Ihnen sagen, dass die von Puppeteer gesteuerte Chrome-Instanz genau wie Ihr alltäglicher Browser die heruntergeladene Datei im Standard-Download-Ordner speichert, nämlich:

  • \Benutzer\<Benutzername>\Downloads für Windows
  • /Users/<Benutzername>/Downloads für Mac
  • /home/<Benutzername>/Downloads für Linux

In diesem Sinne beginnen wir mit dem Programmieren. Nehmen wir an, wir sind Astrophysiker und müssen einige Daten von der NASA sammeln, die wir später verarbeiten werden. Konzentrieren wir uns vorerst auf das Herunterladen der .doc-Dateien.

#1: „Klickbare“ Elemente identifizieren

Wir navigieren hier zur Domain der NASA und untersuchen die Elemente der Seite. Unser Fokus liegt darauf, „anklickbare“ Elemente zu identifizieren. Um die Elemente zu finden, öffnen Sie die Entwicklertools (Befehlstaste + Optionstaste + I / Strg + Umschalt + I in Chrome):

#2: Das Projekt programmieren

import puppeteer from "puppeteer"

(async () => {

   const browser = await puppeteer.launch({ headless: false })

   const page = await browser.newPage()

   await page.goto('https://www.nasa.gov/centers/dryden/research/civuav/civ_uav_doc-n-ref.html',

       { waitUntil: 'networkidle0' })

   const tr_elements = await page.$x('html/body/div[1]/div[3]/div[2]/div[2]/div[5]/div[1]/table[2]/tbody/tr')

   for (let i = 2; i<=tr_elements.length; i ++) {

       const text = await tr_elements[i].evaluate(el => el.textContent)

       if (text.toLocaleLowerCase().includes('doc')) {

           try {

               await page.click(`#backtoTop > div.box_710_cap > div.box_710.box_white.box_710_white > div.white_article_wrap_detail.text_adjust_me > div.default_style_wrap.prejs_body_adjust_detail > table:nth-child(6) > tbody > tr:nth-child(${i}) > td:nth-child(3) > a`)

           }catch {}

       }

   }

   await browser.close()

})()

Was wir hier tun, ist Folgendes:

  • Puppeteer starten und zur gewünschten Website navigieren
  • Alle `tr`-Elemente auswählen, die das `href` enthalten, auf das wir später klicken möchten
  • Die `tr`-Elemente durchlaufen und a. Prüfen, ob der Text innerhalb des Elements das Wort „doc“ enthält b. Wenn ja, erstellen wir den Selektor und klicken auf das Element
  • Den Browser schließen

Und das war’s schon. Das Herunterladen von Dateien mit Puppeteer kann nicht einfacher sein.

Dateien in Puppeteer mit CDP herunterladen

Ich weiß, dass der Standard-Download-Ordner für kleine Projekte kein großes Problem darstellt. Bei größeren Projekten hingegen wirst du die mit Puppeteer heruntergeladenen Dateien sicherlich in verschiedenen Verzeichnissen organisieren wollen. Und genau hier kommt CDP ins Spiel. Um dieses Ziel zu erreichen, behalten wir den aktuellen Code bei und ergänzen ihn lediglich.

Als Erstes fällt einem ein, den Pfad zum aktuellen Verzeichnis aufzulösen. Glücklicherweise können wir das integrierte Modul `node:path` verwenden. Wir müssen lediglich das Modul `path` in unser Projekt importieren und die Methode `resolve` verwenden, wie Sie gleich sehen werden.

Der zweite Aspekt ist die Einstellung des Pfads in unserem Browser mithilfe von CDP. Wie bereits erwähnt, verwenden wir die Methode `.setDownloadBehavior` der Page Domain. So sieht unser aktualisierter Code mit den beiden Ergänzungen aus:

import puppeteer from "puppeteer"

import path from 'path'

(async () => {

   const browser = await puppeteer.launch({ headless: false })

   const page = await browser.newPage()

   const client = await page.target().createCDPSession()

   await client.send('Page.setDownloadBehavior', {

       behavior: 'allow',

       downloadPath: path.resolve('./documents')

   });

   await page.goto('https://www.nasa.gov/centers/dryden/research/civuav/civ_uav_doc-n-ref.html',

       { waitUntil: 'networkidle0' })

   const tr_elements = await page.$x('html/body/div[1]/div[3]/div[2]/div[2]/div[5]/div[1]/table[2]/tbody/tr')

   for (let i = 1; i<=tr_elements.length; i ++) {

       const text = await tr_elements[i].evaluate(el => el.textContent)

       if (text.toLocaleLowerCase().includes('doc')) {

           try {

               await page.click(`#backtoTop > div.box_710_cap > div.box_710.box_white.box_710_white > div.white_article_wrap_detail.text_adjust_me > div.default_style_wrap.prejs_body_adjust_detail > table:nth-child(6) > tbody > tr:nth-child(${i}) > td:nth-child(3) > a`)

           } catch {}

       }

   }

   await browser.close()

})()

Das erreichen wir mit dem hinzugefügten Code:

  • Wir erstellen eine neue CDPSession, um das „Raw Chrome Devtools Protocol“ zu nutzen
  • Wir lösen das `Page.setDownloadBehavior`-Ereignis aus, wobei a. `behavior` auf `allow`-Downloads gesetzt ist b. `downloadPath` mit `node:path` aufgebaut wird, um auf den Ordner zu verweisen, in dem wir unsere Dateien speichern

Und das ist alles, was Sie tun müssen, wenn Sie das Verzeichnis ändern möchten, in dem Dateien mit Puppeteer gespeichert werden. Darüber hinaus haben wir auch unser Ziel erreicht, einen Web-Scraper zum Herunterladen von Dateien zu erstellen.

Dateien mit Puppeteer und Axios herunterladen

Die dritte Option, die wir besprochen haben, besteht darin, Links zu den Zielseiten zu sammeln und diese mit einem HTTP-Client herunterzuladen. Ich persönlich bevorzuge Axios, aber es gibt auch Alternativen dazu, wenn es um Web-Scraping geht. Für die Zwecke dieses Tutorials werde ich also Axios verwenden und davon ausgehen, dass wir einen Scraper für Bilder von Auktionsautos erstellen.

const get_links = async (url) => {

   const hrefs = []

   const browser = await puppeteer.launch({ headless: false })

   const page = await browser.newPage()

   await page.goto(url, { waitUntil: 'networkidle0' })

   const images = await page.$$('img')

   for (let i = 1; i<=images.length; i ++) {

       try {

          hrefs.push(await images[i].evaluate(img => img.src))

      } catch {}

   }

   await browser.close()

   return hrefs

}

Ich bin mir sicher, dass Sie mittlerweile bereits mit der Puppeteer-Syntax vertraut sind. Im Gegensatz zu den obigen Skripten werten wir nun `img`-Elemente aus, extrahieren deren Quell-URL, fügen sie einem Array hinzu und geben das Array zurück. An dieser Funktion ist nichts Besonderes.

#2: Dateien mit Axios speichern

const download_file = async (url, save) => {

   const writer = fs.createWriteStream(path.resolve(save))

   const response = await axios({

       url,

       method: 'GET',

       responseType: 'stream'

   })

   response.data.pipe(writer)

   return new Promise((resolve, reject) => {

       writer.on('finish', resolve)

       writer.on('error', reject)

   })

}

Diese Funktion ist etwas komplexer, da sie zwei neue Pakete einführt: `fs` und `axios`. Die erste Methode aus dem `fs`-Paket ist ziemlich selbsterklärend. Sie erstellt einen beschreibbaren Stream. Mehr dazu erfährst du hier.

Als Nächstes nutzen wir axios, um die URL des Servers anzuzapfen, und teilen axios mit, dass die Antwort vom Typ „stream“ sein wird. Da wir schließlich mit einem Stream arbeiten, verwenden wir `pipe()`, um die Antwort in unseren Stream zu schreiben.

Mit dieser Konfiguration müssen wir nur noch die beiden Funktionen zu einem lauffähigen Programm zusammenfügen. Füge dazu einfach die folgenden Codezeilen hinzu:

let i = 1

const images = await get_links('https://www.iaai.com/Search?url=PYcXt9jdv4oni5BL61aYUXWpqGQOeAohPK3E0n6DCLs%3d')

images.forEach(async (img) => {

   await download_file(img, `./images/${i}.svg`)

   i += 1

})

Fazit

Die Dokumentation von Puppeteer ist in Bezug auf das Herunterladen von Dateien möglicherweise unklar. Trotzdem haben wir heute einige Möglichkeiten entdeckt, dies zu implementieren. Beachten Sie jedoch, dass das Scrapen von Dateien mit Puppeteer keine wirklich einfache Aufgabe ist. Puppeteer startet nämlich einen Headless-Browser, und diese werden in der Regel sehr schnell blockiert.

Wenn Sie nach einer unauffälligen Möglichkeit suchen, Dateien programmgesteuert herunterzuladen, sollten Sie sich vielleicht einem Web-Scraping-Dienst zuwenden. Bei Web Scraping API haben wir viel Zeit und Mühe investiert, um unsere Spuren zu verbergen, damit wir nicht entdeckt werden. Und das zeigt sich in unserer Erfolgsquote. Das Herunterladen von Dateien mit Web Scraping API kann so einfach sein wie das Senden einer Curl-Anfrage, und wir kümmern uns um den Rest.

Vor diesem Hintergrund hoffe ich sehr, dass Ihnen der heutige Artikel bei Ihrem Lernprozess geholfen hat. Außerdem hoffe ich, dass wir unsere beiden ursprünglichen Ziele erreicht haben. Ab sofort sollten Sie in der Lage sein, Ihre eigenen Tools zu erstellen und Dateien mit Puppeteer herunterzuladen.

Ü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.