Ein Headless-Browser ist eine echte Browser-Engine, die ohne sichtbares Fenster läuft: Sie lädt Seiten, führt JavaScript aus, wendet CSS an und baut dasselbe DOM auf, das Chrome oder Firefox erzeugen würden, tut dies jedoch im Hintergrund unter der Kontrolle Ihres Skripts. Für das Headless-Browser-Web-Scraping ist das entscheidend, weil so vieles im modernen Web erst nach dem Ausführen von JavaScript existiert. Eine einfache HTTP-Anfrage liefert Ihnen die anfängliche HTML-Hülle; ein Headless-Browser liefert die Seite, die ein Mensch tatsächlich sieht.

Dieser Leitfaden ist eine praktische, ausführbare Anleitung. Sie werden einen modernen Headless-Stack in Node (Puppeteer, dann Playwright) aufsetzen, eine JavaScript-lastige Seite laden, auf den richtigen Inhalt warten, strukturierte Daten extrahieren und einen Screenshot aufnehmen. Anschließend werden wir ehrlich darüber, wo dieser Ansatz bei größerem Maßstab an seine Grenzen stößt, und zeigen die Ein-Aufruf-Alternative: das serverseitige Rendern einer Seite über die Crawlbase Crawling API mit einem JavaScript-Token.

Was ein Headless-Browser wirklich ist

Ein normaler Browser zeichnet Pixel auf einen Bildschirm. Ein Headless-Browser überspringt die sichtbare Benutzeroberfläche, behält aber alles darunter: die JavaScript-Engine, die Layout-Engine, den Netzwerk-Stack, Cookies und das vollständige DOM. Sie steuern ihn programmatisch anstatt durch Klicken, weshalb er ideal für automatisierte Tests, die Erstellung von Screenshots und das Scraping von Seiten ist, die sich im Client aufbauen.

Modernes Headless Chrome und Firefox liefern denselben Rendering-Code wie ihre sichtbaren Gegenstücke, sodass sich eine Seite genauso verhält wie für einen echten Besucher. Diese Treue ist der eigentliche Punkt: Wenn eine Seite ihren Inhalt erst nach der initialen Antwort mit fetch-Aufrufen lädt, wird nur etwas, das dieses JavaScript ausführt, die Daten je sehen.

Warum JavaScript-lastige Seiten einfache HTTP-Scraper überfordern

Wenn Sie eine Single-Page-App oder ein Infinite-Scroll-Listing mit einem einfachen HTTP-Client anfordern, erhalten Sie normalerweise den Status 200 und einen fast leeren Body. Das gesuchte Markup ist in dieser Antwort nicht enthalten. Es wird erst injiziert, nachdem der Browser die Skripte der Seite ausgeführt, seine XHR- oder fetch-Aufrufe durchgeführt und das Ergebnis in das DOM gerendert hat.

Tools wie Cheerio oder Beautiful Soup parsen das HTML, das Sie ihnen geben, können aber kein JavaScript ausführen, sodass sie nur diese leere Hülle sehen. Ein Headless-Browser schließt diese Lücke: Er führt die Seite genauso aus wie der Browser eines echten Besuchers und lässt Sie dann das fertige DOM auslesen. Für statische, serverseitig gerenderte Seiten brauchen Sie diesen Overhead nicht, aber bei allem, was clientseitig gerendert wird, ist es der Unterschied zwischen Daten und einem leeren Array.

Headless vs. einfaches HTTP

Greifen Sie auf einen Headless-Browser zurück, wenn der gewünschte Inhalt erst nach dem Ausführen von Skripten erscheint, wenn Sie klicken oder scrollen müssen, um mehr anzuzeigen, oder wenn Sie einen Screenshot benötigen. Für statisches HTML, das Ihre Daten bereits enthält, ist ein einfacher HTTP-Fetch plus einem Parser schneller und günstiger. Passen Sie das Werkzeug an die Seite an, nicht umgekehrt.

Das Projekt einrichten

Sie benötigen Node.js (Version 18 oder neuer) und npm. Überprüfen Sie beides, erstellen Sie ein Projekt und installieren Sie Puppeteer. Puppeteer lädt bei der Installation einen kompatiblen Chrome-Build für Sie herunter, sodass nichts weiter zu konfigurieren ist.

bash
node --version
npm --version

mkdir headless-scraper && cd headless-scraper
npm init -y
npm install puppeteer

Eine wichtige Vorabinformation: Ein Headless-Browser ist ressourcenintensiv. Jede Instanz ist ein vollständiger Chrome-Prozess mit eigenem Speicher- und CPU-Bedarf. Auf dem eigenen Laptop für eine Seite auf einmal ist das in Ordnung, und es wird zum zentralen Skalierungsproblem, sobald Sie Hunderte von Seiten parallel verarbeiten wollen. Wir kommen darauf zurück.

Einen Headless-Browser starten und eine Seite laden

Der grundlegende Ablauf mit Puppeteer ist immer derselbe: Browser starten, neue Seite öffnen, zu einer URL navigieren, die Arbeit erledigen und dann den Browser schließen, damit keine Prozesse offenbleiben. Hier ist die minimale Version, die eine Seite lädt und ihren Titel ausgibt.

javascript
const puppeteer = require('puppeteer')

async function run() {
  const browser = await puppeteer.launch({ headless: true })
  const page = await browser.newPage()

  await page.goto('https://quotes.toscrape.com/js/', {
    waitUntil: 'networkidle2',
  })

  console.log(await page.title())
  await browser.close()
}

run().catch((err) => console.error(err))

Führen Sie es mit node index.js aus. Das Ziel hier, die JavaScript-Version von Quotes to Scrape, rendert seine Zitate absichtlich clientseitig, weshalb es sich als sauberes Testfeld eignet: Ein einfacher Fetch gibt eine leere Liste zurück, während der Headless-Browser echten Inhalt sieht. Die Option waitUntil: 'networkidle2' weist goto an, erst aufzulösen, wenn das Netzwerk einen Moment ruhig war, was Ihre erste und gröbste Wartestrategie ist.

Auf den richtigen Inhalt warten, nicht auf einen festen Timer

Das Warten ist die Stelle, an der die meisten Headless-Scraper scheitern. Ein festes sleep ist fragil: zu kurz und Sie parsen, bevor die Daten angekommen sind, zu lang und jeder Durchlauf kriecht. Der bessere Ansatz ist, auf ein spezifisches Signal zu warten, das bestätigt, dass der gewünschte Inhalt tatsächlich vorhanden ist.

Puppeteer bietet Ihnen mehrere Optionen, in ungefährer Reihenfolge ihrer Eignung:

  • waitForSelector blockiert, bis ein bestimmtes Element im DOM erscheint. Dies ist das zuverlässigste Signal, weil es die Wartezeit an die gewünschten Daten knüpft.
  • waitForFunction blockiert, bis eine beliebige JavaScript-Bedingung wahr ist, zum Beispiel wenn eine Liste eine bestimmte Länge erreicht. Verwenden Sie es, wenn die bloße Anwesenheit nicht ausreicht.
  • waitUntil bei goto (load, domcontentloaded, networkidle2) steuert, wann die Navigation aufgelöst wird. Gut als Basiswert, schwach als einzige Garantie.

Bevorzugen Sie das Warten auf einen Selektor gegenüber einem festen Timer, wo immer möglich. Das ist im Durchschnitt schneller und weit robuster, wenn das Netzwerk langsam ist.

javascript
await page.goto('https://quotes.toscrape.com/js/', {
  waitUntil: 'domcontentloaded',
})

// Block until the first quote is actually in the DOM.
await page.waitForSelector('.quote')

// Or wait for a richer condition: at least 10 quotes loaded.
await page.waitForFunction(() => {
  return document.querySelectorAll('.quote').length >= 10
})

Strukturierte Daten aus dem gerenderten DOM extrahieren

Sobald der Inhalt vorhanden ist, führt page.evaluate eine Funktion im eigenen Kontext der Seite aus, wo Sie das vollständige DOM und die standardmäßigen Browser-APIs haben. Was Sie zurückgeben, wird an Ihr Node-Skript zurückübertragen. Das hält die Extraktion einfach: Sie schreiben gewöhnlichen querySelectorAll-Code, als wären Sie in der Browser-Konsole.

javascript
const quotes = await page.evaluate(() => {
  const cards = document.querySelectorAll('.quote')

  return Array.from(cards).map((card) => ({
    text: card.querySelector('.text').innerText.trim(),
    author: card.querySelector('.author').innerText.trim(),
    tags: Array.from(card.querySelectorAll('.tag')).map((t) => t.innerText),
  }))
})

console.log(quotes)

Das Ergebnis ist ein sauberes Array von Objekten, das Sie als JSON speichern, in eine Datenbank schreiben oder in eine Pipeline einspeisen können. Ein gekürztes Beispiel der Ausgabe sieht so aus:

json
[
  {
    "text": "The world as we have created it is a process of our thinking.",
    "author": "Albert Einstein",
    "tags": ["change", "deep-thoughts", "thinking"]
  },
  {
    "text": "It is our choices that show what we truly are.",
    "author": "J.K. Rowling",
    "tags": ["abilities", "choices"]
  }
]

Einen Screenshot aufnehmen

Etwas, das nur eine echte Rendering-Engine bieten kann, ist ein originalgetreuer Screenshot, der für visuelle Qualitätsprüfung, das Archivieren des Seitenzustands oder das Debuggen eines Scrapes nützlich ist, der nichts zurückgegeben hat. Puppeteer erfasst den Viewport oder die vollständige scrollbare Seite in einem einzigen Aufruf.

javascript
await page.screenshot({
  path: 'quotes.png',
  fullPage: true,
})

Wenn Screenshots das Hauptziel bei größerem Volumen sind, ist das Betreiben und Warten einer Browser-Flotte nur für Bilder übertrieben. Die Crawlbase Screenshots API rendert die Seite serverseitig und gibt das Bild direkt zurück, ohne dass Sie auf Ihrer Seite einen Browser verwalten müssen.

Dieselbe Aufgabe in Playwright

Playwright, gewartet von Microsoft, ist die andere moderne Wahl. Es steuert Chromium, Firefox und WebKit über eine einzige API, und sein Auto-Warte-Verhalten macht viele der oben genannten expliziten Wartezeiten überflüssig: Aktionen wie click und Locator-Lesevorgänge warten standardmäßig darauf, dass das Element bereit ist. Die Struktur spiegelt Puppeteer eng wider, weshalb das Portieren zwischen beiden unkompliziert ist.

javascript
const { chromium } = require('playwright')

async function run() {
  const browser = await chromium.launch({ headless: true })
  const page = await browser.newPage()

  await page.goto('https://quotes.toscrape.com/js/')
  await page.waitForSelector('.quote')

  const quotes = await page.$$eval('.quote .text', (els) =>
    els.map((el) => el.innerText.trim()),
  )

  console.log(quotes)
  await browser.close()
}

run().catch((err) => console.error(err))

Beide Bibliotheken sind ausgezeichnet. Für einen ausführlicheren Vergleich mit einer anderen Sprache und einem anderen Tool führt Web Scraping mit Python und Selenium dieselben Konzepte in einem Selenium- und Python-Build durch.

Wo Headless-Scraping bei größerem Maßstab an Grenzen stößt

Alles oben Genannte funktioniert wunderbar für eine Seite auf Ihrem Rechner. Die Probleme beginnen, wenn Sie Volumen benötigen, und sie zeigen sich auf zwei verschiedene Arten.

Erstens die Ressourcen. Jede Headless-Browser-Instanz ist ein vollständiger Chrome-Prozess, der Hunderte von Megabytes RAM verbraucht. Eine Handvoll parallel zu betreiben ist in Ordnung; genug zu betreiben, um Tausende von Seiten pro Stunde zu scrapen, bedeutet eine Flotte aufzubauen, Speicherlecks und Zombie-Prozesse zu verwalten, abgestürzte Instanzen neu zu starten und für die darunter liegende Infrastruktur zu bezahlen. Der Browser, der einst ein einzeiliger launch-Aufruf war, wird zur Infrastrukturaufgabe.

Zweitens die Anti-Bot-Abwehr. Ernsthafte kommerzielle Seiten rendern nicht nur Inhalte, sie suchen aktiv nach Automatisierung. Standard-Headless-Browser hinterlassen Signale (das navigator.webdriver-Flag, fehlende oder ungewöhnliche Browser-Fingerprints, Rechenzentrum-IPs), die Erkennungssysteme sofort lesen. Am Ende fügen Sie Stealth-Plugins hinzu, rotieren Residential Proxies, damit Anfragen von echten Nutzer-IPs kommen, und lösen CAPTCHAs, und jedes davon ist seine eigene laufende Wartungsbelastung. Das Scraping selbst hört auf, der schwierige Teil zu sein.

Für das umfassendere Playbook zum Umgang mit Sperren siehe wie man Websites scrapt, ohne blockiert zu werden. Kurz gesagt: Rendering lässt sich eigenständig lösen, aber zuverlässiges Rendering bei großem Maßstab von IPs, denen ein Ziel vertraut, ist ein anderes und weitaus größeres Problem.

Die Ein-Aufruf-Alternative: Crawling API mit einem JS-Token

Das ist der Schmerzpunkt, den eine verwaltete API beseitigt. Anstatt eine eigene Browser-Flotte zu betreiben und zu härten, senden Sie eine URL an die Crawlbase Crawling API mit einem JavaScript-Token. Die API rendert die Seite in einem echten Browser auf ihrer Seite, hinter einem rotierenden Pool vertrauenswürdiger Residential-IPs, und gibt das fertige HTML zurück, das Sie parsen können. Rendering und das IP-Problem kollabieren in einer einzigen Anfrage.

Installieren Sie den Client und führen Sie einen Aufruf durch. Erstellen Sie ein Crawlbase-Konto, holen Sie Ihr JavaScript-Token aus dem Dashboard und fügen Sie es an der Stelle ein, wo Sie YOUR_CRAWLBASE_JS_TOKEN sehen.

bash
npm install crawlbase cheerio
javascript
const { CrawlingAPI } = require('crawlbase')
const cheerio = require('cheerio')

const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_JS_TOKEN' })

const options = {
  ajax_wait: true,
  page_wait: 5000,
}

async function scrape() {
  const response = await api.get('https://quotes.toscrape.com/js/', options)
  const $ = cheerio.load(response.body)

  const quotes = []
  $('.quote').each((i, el) => {
    quotes.push({
      text: $(el).find('.text').text().trim(),
      author: $(el).find('.author').text().trim(),
    })
  })

  console.log(quotes)
}

scrape().catch((err) => console.error(err))

Die Wartestrateg ien, die Sie mit Puppeteer gelernt haben, haben hier direkte Entsprechungen. Die Option ajax_wait weist die API an, auf asynchronen Inhalt zu warten, bevor sie zurückgibt, und page_wait hält für eine feste Anzahl von Millisekunden nach dem Laden an, damit spät gerenderte Elemente erscheinen. Für Seiten, die Inhalt hinter einem Button verbergen, nimmt css_click_selector einen URL-kodierten CSS-Selektor entgegen und klickt nach dem Rendering darauf, dieselbe Idee wie ein Puppeteer page.click gefolgt von einem Wait.

Warum speziell das JS-Token

Crawlbase bietet zwei Token-Typen an. Das normale Token ruft statisches HTML ab; das JavaScript-Token (JS) rendert die Seite zunächst in einem echten Browser. Für jede clientseitig gerenderte Seite wie die oben genannte benötigen Sie das JS-Token. Das normale Token würde dieselbe leere Hülle zurückgeben wie ein einfacher Fetch.

Crawlbase Crawling API

Rendern Sie JavaScript-lastige Seiten hinter vertrauenswürdigen Residential-IPs in einem einzigen Aufruf. Die Crawling API nimmt ein JS-Token, führt die Seite serverseitig in einem echten Browser aus, rotiert IPs für Sie und gibt fertiges HTML zurück, sodass Sie keine Headless-Flotte, keinen Proxy-Pool und keinen CAPTCHA-Stack selbst betreiben müssen. Probieren Sie es zunächst im kostenlosen Tier aus.

Welchen Ansatz sollten Sie wählen?

Beide haben ihren Platz, und die Entscheidung hängt hauptsächlich vom Volumen und davon ab, wie stark sich das Ziel verteidigt.

Betreiben Sie Ihren eigenen Headless-Browser, wenn Sie feingranulare Kontrolle über eine Seite benötigen: komplexe mehrstufige Interaktionen, das Anmelden in Ihren eigenen Konten zu Testzwecken, das Erstellen von Screenshots für eine kleine Anzahl von Seiten oder das Scraping einer Handvoll freundlicher Seiten, bei denen Sperren kein Problem sind. Die Kontrolle ist unübertroffen und die Kosten sind bei kleinem Maßstab gering.

Greifen Sie auf die verwaltete Crawling API zurück, wenn Sie in großem Umfang scrapen, wenn das Ziel Bots aktiv blockiert oder wenn Sie einfach keine Browser- und Proxy-Infrastruktur besitzen wollen. Wenn Sie reine IP-Rotation ohne Rendering benötigen, deckt der Smart AI Proxy das ab; wenn Sie geparste JSON-Daten für unterstützte Seiten anstelle von rohem HTML möchten, übernimmt auch die Crawling API die Extraktion. Der Punkt ist, Ihre Zeit mit den Daten zu verbringen, nicht damit, eine Flotte am Leben zu erhalten.

Zusammenfassung

Wichtigste Erkenntnisse

  • Ein Headless-Browser führt die vollständige Seite aus. Er führt JavaScript aus und baut das echte DOM auf, sodass er Inhalte sieht, die ein einfacher HTTP-Fetch niemals sehen wird.
  • Warten Sie auf einen Selektor, nicht auf einen Timer. waitForSelector und waitForFunction knüpfen die Wartezeit an die gewünschten Daten und sind weit robuster als ein festes Sleep.
  • Extraktion erfolgt im Seitenkontext. page.evaluate (oder Playwrights $$eval) führt DOM-Code in der Seite aus und gibt saubere strukturierte Objekte zurück.
  • Skalierung ist der eigentliche Kostentreiber. Browser-Flotten verbrauchen Speicher, und Anti-Bot-Abwehr erzwingt Stealth, Proxies und CAPTCHA-Behandlung obendrauf.
  • Ein JS-Token löst beide Probleme. Die Crawling API rendert serverseitig hinter vertrauenswürdigen IPs und gibt fertiges HTML in einem Aufruf zurück.
  • Passen Sie das Werkzeug an die Aufgabe an. Eigenentwicklung für Kontrolle bei kleinem Maßstab; eine verwaltete API für Volumen und schwierige Ziele.

Häufig gestellte Fragen

Was ist ein Headless-Browser beim Web Scraping?

Ein Headless-Browser ist eine echte Browser-Engine, wie Chrome oder Firefox, die ohne sichtbares Fenster läuft. Beim Web Scraping lädt er eine Seite, führt ihr JavaScript aus und baut dasselbe DOM auf, das ein Mensch sehen würde, was Ihnen ermöglicht, Inhalte zu extrahieren, die erst nach dem Ausführen von Skripten erscheinen. Sie steuern ihn per Code statt durch Klicken, weshalb er ideal für JavaScript-lastige Seiten ist, die ein einfacher HTTP-Request nicht lesen kann.

Sollte ich Puppeteer oder Playwright für Headless-Scraping verwenden?

Beide sind ausgezeichnet und sehr ähnlich. Puppeteer konzentriert sich auf Chrome und Firefox und ist einfach zu starten. Playwright steuert Chromium, Firefox und WebKit über eine einzige API und hat stärkeres eingebautes Auto-Warten, was viel manuellen Wait-Code überflüssig macht. Wählen Sie Playwright, wenn Sie browserübergreifende Abdeckung benötigen oder sein Locator-Modell bevorzugen; wählen Sie Puppeteer für ein schlankes Chrome-only-Setup. Die Konzepte in diesem Leitfaden gelten für beide.

Wie wartet man am besten auf Inhalte in einem Headless-Browser?

Warten Sie auf ein bestimmtes Element statt auf einen festen Timer. Verwenden Sie waitForSelector, um zu blockieren, bis das gewünschte Element im DOM ist, oder waitForFunction für eine reichhaltigere Bedingung wie eine Liste, die eine bestimmte Länge erreicht. Feste Sleeps sind fragil: zu kurz und Sie parsen zu früh, zu lang und jeder Durchlauf zieht sich. Das Knüpfen der Wartezeit an Ihre Zieldaten ist im Durchschnitt schneller und zuverlässiger.

Warum werden Headless-Browser blockiert?

Standard-Headless-Browser hinterlassen Automatisierungssignale: das navigator.webdriver-Flag, ungewöhnliche oder fehlende Fingerprints und Rechenzentrum-IP-Adressen, die Erkennungssysteme sofort markieren. Ernsthafte Seiten achten genau darauf. Die Abhilfe bedeutet das Hinzufügen von Stealth-Konfiguration, das Rotieren von Residential Proxies, damit Anfragen von echten Nutzer-IPs kommen, und das Behandeln von CAPTCHAs, jedes davon ist laufende Arbeit. Eine verwaltete API, die hinter vertrauenswürdigen IPs rendert, erledigt das für Sie.

Kann ich mit einem Headless-Browser Screenshots aufnehmen?

Ja. Sowohl Puppeteer als auch Playwright erfassen den Viewport oder die vollständige scrollbare Seite in einem Aufruf, was für visuelle Qualitätsprüfung, Archivierung und das Debuggen leerer Scrapes nützlich ist. Wenn Screenshots Ihr Hauptbedarf bei großem Volumen sind, ist das Betreiben einer Browser-Flotte nur für Bilder übertrieben; die Screenshots API rendert serverseitig und gibt das Bild direkt zurück, ohne Browser-Verwaltung.

Wann sollte ich die Crawling API anstelle eines eigenen Browsers verwenden?

Verwenden Sie Ihren eigenen Headless-Browser für feingranulare Kontrolle bei kleinem Maßstab oder auf freundlichen Seiten. Wechseln Sie zur Crawling API, wenn Sie in großem Umfang scrapen, auf aggressive Anti-Bot-Abwehr stoßen oder keine Browser- und Proxy-Infrastruktur besitzen wollen. Mit einem JS-Token rendert sie die Seite serverseitig hinter rotierenden Residential-IPs und gibt fertiges HTML in einem Aufruf zurück, sodass Sie die Flotte, den Proxy-Pool und den CAPTCHA-Stack überspringen.

Jetzt loslegen

Crawlen Sie jede Website im großen Maßstab, ohne gegen die Infrastruktur zu kämpfen.

Crawlbase übernimmt Proxys, Fingerprints und CAPTCHAs, damit Ihr Team Datenpipelines ausliefert, statt Crawl-Infrastruktur zu pflegen. 1.000 Anfragen kostenlos, keine Karte erforderlich.

Self-Service · Kein Verkaufsgespräch erforderlich · Enterprise-Crawl-Volumen verfügbar