Scraper gibt eine leere Seite zurück? Es ist JavaScript. So beheben Sie es.

Das body-Tag ist in Ihrem Scraper leer, aber in den DevTools voll. Das ist kein Fehler – es ist clientseitiges Rendering. So erkennen Sie es, drei Möglichkeiten, die Daten zu erhalten, und wie Sie vermeiden, einen Browser zu starten, den Sie nicht benötigen.

Sie rufen die Seite ab, drucken das HTML, und der Teil, den Sie wollten, fehlt – ein leeres <body>, eine leere Knotenliste oder ein Hinweis, dass "JavaScript erforderlich ist." Währenddessen zeigt der Browser-Inspektor die Daten direkt an. Das ist kein Scraper-Fehler. Es ist eine Seite, die ihre Inhalte clientseitig mit JavaScript rendert, und Ihre HTTP-Anfrage erhält nur die leere Hülle, die vor dem JavaScript läuft. So bestätigen Sie das, drei Wege, um die Daten trotzdem zu erhalten, und wie Sie vermeiden, einen Headless-Browser zu starten, den Sie eigentlich nicht benötigen.

Warum der Body leer ist

Ein roher HTTP-Client — Python requests, Node fetch, cURL — lädt das HTML herunter, das der Server sendet, und stoppt. Er führt kein JavaScript aus. Auf React, Vue, Angular oder Next.js basierende Seiten liefern ein nahezu leeres HTML-Skelett plus ein Bundle, das Daten abruft und das DOM im Browser aufbaut. Parser wie BeautifulSoup oder Cheerio sehen das Skelett, sodass ein Selektor, der in den DevTools perfekt funktioniert, in Ihrem Code ein leeres Array zurückgibt. Cheerio, das auf einer React-Seite [] zurückgibt, ist das klassische Symptom; es führt kein JavaScript aus und wird es auch nie tun.

import * as cheerio from "cheerio";

const html = await (await fetch("https://shop.example.com/products")).text();
const $ = cheerio.load(html);

console.log($("div.product").length);
// 0  ->  the products are drawn by JavaScript, not in the HTML

Bestätigen Sie es in zehn Sekunden

Bevor Sie zu einem Browser greifen, beweisen Sie die Diagnose. Zwei schnelle Überprüfungen sagen Ihnen genau, womit Sie es zu tun haben: Vergleichen Sie die rohe Antwort mit dem, was der Browser rendert, und zählen Sie Elemente in der Konsole. Wenn die Konsole Knoten findet, die das rohe HTML nicht enthält, sind die Inhalte clientseitig. Wenn das rohe HTML sie bereits hat, sind Ihr Selektor oder Ihre Header das Problem, nicht das Rendering.

# does the raw response actually contain the data?
curl -s https://shop.example.com/products | grep -c 'class="product"'
# 0 -> not in the HTML  |  24 -> it IS there, fix your selector

# then, in the browser DevTools console on the same page:
# document.querySelectorAll('div.product').length   -> 24

Ein weiterer Zweig, den es auszuschließen gilt: Ein leerer Body ist nicht immer JavaScript. Manchmal hält der Server die Inhalte zurück, bis Sie die richtigen Header oder Cookies senden — ein Sitzungs-Cookie, ein Referer, ein Accept-Language oder ein User-Agent, der nicht "Skript" schreit. Wenn cURL eine kurze Seite zurückgibt, aber Ihr Browser (der einen vollständigen Header-Satz sendet) eine vollständige, versuchen Sie, die Anfrage-Header des Browsers zu wiederholen, bevor Sie das Rendering annehmen. Es ist ein fünfminütiger Test, der Sie davor bewahren kann, einen Browser zu starten, den Sie nie benötigt haben.

Checkliste-Diagramm zur Unterscheidung von statischen HTML-Seiten und JavaScript-gerenderten Seiten anhand von Anzeichen wie leeren Body-Tags, eingebettetem Status-JSON und XHR-Aufrufen
Leerer Body, ein Framework-Status-Blob im rohen HTML oder XHR-Aufrufe im Netzwerk-Tab: drei zuverlässige Anzeichen dafür, dass die Seite clientseitig rendert.

Fix 1: das versteckte JSON-API aufrufen (am schnellsten)

Hier ist das, was die meisten Leute übersehen: Wenn sich eine Seite mit JavaScript selbst zeichnet, kommen die Daten von irgendwoher — normalerweise von einem internen JSON-Endpunkt, den das Frontend nach dem Laden aufruft. Öffnen Sie den Netzwerk-Tab, filtern Sie nach Fetch/XHR, laden Sie neu und suchen Sie nach der Anfrage, die Ihre Daten zurückgibt. Das direkte Aufrufen dieses Endpunkts ist schneller, leichter und weit weniger anfällig als das Parsen von HTML, da Sie den gesamten Browser überspringen und direkt strukturiertes JSON erhalten. Manchmal benötigt der Endpunkt ein Token, das im anfänglichen HTML eingebettet ist — holen Sie es von der Seite und rufen Sie dann die API auf.

// 1) the SPA loads data from its own endpoint after paint
const res = await fetch("https://shop.example.com/api/catalog?page=1", {
  headers: { accept: "application/json", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" },
});
const { products } = await res.json();
console.log(products.length); // real data, no browser

// 2) if it needs a token, the initial HTML usually carries it
const page = await (await fetch("https://shop.example.com/")).text();
const token = page.match(/"s_token":\s*"([^"]+)"/)?.[1];

Fix 2: das Hydration-JSON extrahieren

Selbst wenn es keinen separaten API-Aufruf gibt, betten Frameworks den anfänglichen Status direkt im HTML ein, damit der Client ohne Roundtrip "hydratisieren" kann. Next.js liefert es in einem <script id="__NEXT_DATA__">-Tag; andere Stacks verwenden window.__INITIAL_STATE__ oder einen ähnlichen Blob. Dieses JSON befindet sich in der rohen Antwort, die Sie bereits heruntergeladen haben — kein Rendering erforderlich. Parsen Sie das Skript-Tag, und Sie erhalten oft die genau strukturierten Daten, die die Seite angezeigt hätte, sauberer als das Scrapen des gerenderten DOMs.

import * as cheerio from "cheerio";

const html = await (await fetch(url)).text();
const $ = cheerio.load(html);

const blob = $("#__NEXT_DATA__").html();      // Next.js embeds page state here
if (blob) {
  const data = JSON.parse(blob);
  const products = data.props.pageProps.products;
  console.log(products.length);               // structured, from static HTML
}

Zwischen dem versteckten API und dem Hydration-Mining sind die meisten "leere Seite"-Probleme gelöst, ohne jemals einen Browser zu starten. Unser Leitfaden zum Scrapen von JavaScript-intensiven Seiten geht tiefer darauf ein, wie man diese Endpunkte zuverlässig findet.

Ein Wort der Vorsicht bei beiden: Versteckte Endpunkte und Status-Blobs sind inoffizielle Oberflächen, daher ändern sie sich ohne Vorwarnung. Verpacken Sie das Parsen in eine Überprüfung, die laut fehlschlägt, wenn sich die Form ändert — ein fehlender Schlüssel oder ein Array mit null Länge sollte einen Fehler auslösen, nicht stillschweigend nichts zurückgeben. Auf diese Weise zeigt eine Website, die ihre API umstrukturiert, als klarer Fehler in Ihren Logs auf, anstatt als leeres Dataset, das Sie eine Woche lang nicht bemerken. Fixieren Sie auch den User-Agent und die Header, die Sie verwendet haben, als es funktionierte; einige dieser Endpunkte sind stillschweigend darauf angewiesen.

Fix 3: Rendern — aber behandeln Sie es als letzten Ausweg

Wenn die Daten wirklich im DOM aufgebaut werden, ohne dass JSON offengelegt wird, müssen Sie das JavaScript ausführen. Playwright oder Puppeteer steuern einen echten Browser, warten auf die Inhalte und übergeben Ihnen das fertige HTML. Es funktioniert überall, aber es ist die teure Option: Das Rendern kostet etwa 10-50x die Bandbreite und Zeit eines JSON-Aufrufs, und ein Headless-Browser auf einer Rechenzentrums-IP wird schnell blockiert. Leiten Sie es über einen Residential-Proxy und warten Sie auf das richtige Signal, nicht auf einen festen Schlaf.

import { chromium } from "playwright";

const browser = await chromium.launch();
const context = await browser.newContext({
  proxy: { server: "http://gate.quantumproxies.io:8000", username: "USER", password: "PASS" },
});
const page = await context.newPage();
await page.goto(url, { waitUntil: "networkidle", timeout: 30000 });
await page.waitForSelector("div.product");     // wait for data, not a sleep
const html = await page.content();
await browser.close();

Rendern und rotieren in einem Aufruf mit Scraper API

Flussdiagramm der Fix-Leiter für JavaScript-gerenderte Seiten: verstecktes JSON-API, Hydration-Mining, Rendern mit Proxy, dann Scraper API
Klettern Sie die Leiter hinunter: Versuchen Sie zuerst das versteckte API und das Hydration-JSON, und rendern Sie nur, wenn beide fehlschlagen.

Wann sich das eigene Rendern nicht mehr lohnt

Das Betreiben einer eigenen Browserflotte bedeutet, Headless-Erkennung, Proxy-Rotation, Speicherlecks und Warte-Logik für jedes Ziel zu verwalten. Sobald Sie das tun, ist eine Scraper API, die JavaScript rendert, Residential-IPs rotiert und sauberes HTML, JSON oder Markdown in einer einzigen Anfrage zurückgibt, normalerweise weniger Code und eine höhere Erfolgsquote — und sie kann KI-Extraktion auf der gerenderten Seite ausführen, sodass Sie Selektoren vollständig überspringen. Unsere Analyse der Renderkosten im Vergleich zu einfachen Anfragen zeigt, wann Sie den Wechsel vornehmen sollten.

Häufig gestellte Fragen

Warum gibt mein Web-Scraper einen leeren Body zurück?

Weil die Seite ihre Inhalte mit JavaScript rendert, nachdem das HTML geladen wurde, und Ihr HTTP-Client kein JavaScript ausführt. Sie erhalten das leere Skelett, das der Server sendet. Bestätigen Sie dies, indem Sie die rohe Antwort (über cURL) mit dem vergleichen, was DevTools zeigt — wenn der Browser Daten hat, die das rohe HTML nicht enthält, ist es clientseitiges Rendering.

Wie scrapt man eine JavaScript-gerenderte Seite ohne Browser?

Zwei Möglichkeiten, einen Browser vollständig zu vermeiden. Finden Sie den internen JSON-Endpunkt, den die Seite im Netzwerk-Tab aufruft, und fordern Sie ihn direkt an, oder extrahieren Sie den Hydrationszustand, der im HTML eingebettet ist (zum Beispiel das __NEXT_DATA__-Skript-Tag). Beide geben Ihnen strukturierte Daten schneller und zuverlässiger als das Rendern.

Führen BeautifulSoup oder Cheerio JavaScript aus?

Nein. Beide parsen den HTML-String, den Sie ihnen geben, und führen kein JavaScript aus. Auf einer React-, Vue- oder Next.js-Seite sehen sie nur das Vorab-Render-Skelett, weshalb Ihr Selektor ein leeres Array zurückgibt. Sie benötigen ein gerendertes DOM (Playwright/Puppeteer) oder das zugrunde liegende JSON, um die Daten zu erhalten.

Warum erhalte ich eine Meldung 'JavaScript ist deaktiviert' beim Scrapen?

Die Seite liefert einen Fallback für Clients, die kein JavaScript ausführen, und Ihr Scraper sieht wie einer aus. Es wird oft mit Bot-Erkennung gepaart. Das Ausführen der Seite in einem Headless-Browser auf einer sauberen Residential-IP klärt es; wenn es erneut auftritt, fingerprintet das Ziel die Anfrage, nicht nur die Überprüfung auf JavaScript.

Eine leere Seite ist eine Diagnose, kein Ende. Bestätigen Sie, dass es clientseitig ist, greifen Sie zuerst zum versteckten API oder Hydration-JSON, und rendern Sie nur, wenn die Daten nirgendwo anders leben. Sie werden weniger Code schreiben, einen Bruchteil der Bandbreite nutzen und weit seltener blockiert. Wenn Sie nichts davon selbst verwalten möchten, übernimmt die Scraper API das Rendering und die Rotation für Sie.

Probieren Sie die QuantumProxies Scraper API