Wer in JavaScript zuhause ist, hat bereits das meiste, was benötigt wird, um strukturierte Daten aus dem Web zu ziehen. Node.js liefert eine schnelle Laufzeit, ein riesiges Paket-Ökosystem und ein asynchrones I/O-Modell, das viele Anfragen ohne Mühe bewältigt. Legen Sie zwei kleine Bibliotheken darüber und Sie haben einen funktionierenden Scraper in ein paar Dutzend Zeilen.
Diese Anleitung zeigt, wie man auf praktische Weise einen Web-Scraper mit Node.js baut. Wir beginnen mit dem Standard-Stack, axios für HTTP und cheerio für jQuery-artiges HTML-Parsing, und bauen einen Scraper, der eine Seite abruft, die gewünschten Felder selektiert, über eine Liste iteriert und die Ergebnisse in JSON und CSV schreibt. Dann werden wir ehrlich darüber, wo einfaches HTTP an seine Grenzen stößt (JavaScript-gerenderte Seiten und Blocks bei Skalierung) und was dagegen zu tun ist.
Was Sie bauen werden
Ein kleines Node.js-Skript, das eine URL nimmt, das HTML herunterlädt, es mit cheerio parst und pro Element einen sauberen Datensatz extrahiert. Als durchgehendes Beispiel verwenden wir ein generisches Produkt-Listing-Layout, da dieses Muster (eine sich wiederholende Karte mit Titel, Preis und Link) die meisten realen Scraping-Aufgaben abdeckt. Am Ende werden Sie haben:
- Einen Einzelseiten-Fetcher, der auf axios und cheerio aufbaut.
- Einen Extraktor, der CSS-Selektoren auf Felder abbildet.
- Eine Schleife, die paginierte Listenseiten durchläuft und jede Zeile sammelt.
- Ausgabe, die in JSON und CSV geschrieben wird.
- Einen Drop-in-Upgrade-Pfad für Seiten, die Sie blockieren oder clientseitig rendern.
Warum Node.js zum Scrapen
Node.js führt JavaScript außerhalb des Browsers auf Chromes V8-Engine aus, die zu Maschinencode kompiliert und schnell bleibt. Sein nicht-blockierendes, ereignisgesteuertes Modell ist eine natürliche Passform fürs Scrapen, wo man die meiste Zeit auf Netzwerkantworten wartet: Man kann viele Anfragen auf einem einzigen Thread in der Luft halten, ohne pro Verbindung einen Thread zu starten. Dazu das npm-Ökosystem, wo es für fast jeden Parsing-, Warteschlangen- oder Speicherbedarf bereits ein kampferprobtes Paket gibt, und man hat eine Laufzeit, die für diese Art von Arbeit gemacht ist. Unternehmen wie Netflix und PayPal betreiben Node.js aus denselben Gründen in der Produktion.
Die zwei Bibliotheken, die die Hauptarbeit beim statischen Scraping leisten, sind axios (ein Promise-basierter HTTP-Client) und cheerio (ein leichtgewichtiger Parser, der Ihnen jQuery-Selektoren über serverseitiges HTML gibt, ohne Browser). Wenn Sie speziell die Anfrage-Seite auffrischen möchten, lesen Sie HTTP-Anfragen in Node.js mit der Fetch API machen.
Voraussetzungen
Nichts Ausgefallenes. Vor dem Schreiben von Code benötigen Sie drei Dinge.
Grundlegendes JavaScript und Node.js. Sie sollten bequem ein Skript schreiben, es vom Terminal aus ausführen und Pakete mit npm installieren können. Async/await macht den Code lesbar, daher sind Grundkenntnisse von Promises hilfreich.
Node.js 18 oder neuer. Prüfen Sie Ihre Version mit node --version. Falls Sie es nicht haben, installieren Sie das aktuelle LTS von nodejs.org.
Ein Code-Editor. Jeder funktioniert; VS Code ist die gängige Wahl.
Das Projekt einrichten
Erstellen Sie einen Ordner, initialisieren Sie ein Projekt und installieren Sie die zwei Abhängigkeiten.
mkdir node-scraper && cd node-scraper npm init -y npm install axios cheerio
Um moderne import-Syntax zu verwenden, fügen Sie "type": "module" zu Ihrer package.json hinzu. Wenn Sie lieber bei require bleiben, funktioniert der folgende Code auf dieselbe Weise mit CommonJS; tauschen Sie die Import-Zeilen einfach gegen const axios = require("axios") aus.
Schritt 1: Eine Seite abrufen
Beginnen Sie damit, das rohe HTML herunterzuladen. axios gibt den Response-Body bei response.data zurück. Das Setzen eines realistischen User-Agent-Headers lässt die Anfrage wie ein Browser aussehen statt wie ein Standard-Node-Client, was viele Sites mit Misstrauen behandeln.
import axios from "axios"; const fetchPage = async (url) => { const { data } = await axios.get(url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", }, timeout: 15000, }); return data; };
Das timeout verhindert, dass ein langsamer oder toter Host Ihren Lauf für immer blockiert. axios weist das Promise bei jedem Nicht-2xx-Status zurück, sodass das Einwickeln von Aufrufen in try/catch (das tun wir später) Fehler sichtbar statt still hält.
Schritt 2: Mit cheerio parsen und extrahieren
cheerio lädt einen HTML-String und gibt Ihnen eine $-Funktion, die sich wie jQuery verhält. Sie selektieren Elemente per CSS-Selektor und lesen deren Text oder Attribute. Das Muster ist: den sich wiederholenden Container finden, dann für jeden die gewünschten Felder ziehen.
import * as cheerio from "cheerio"; const parseProducts = (html) => { const $ = cheerio.load(html); const products = []; $(".product-card").each((_, el) => { const card = $(el); products.push({ title: card.find("h2.title").text().trim(), price: card.find(".price").text().trim(), url: card.find("a").attr("href"), }); }); return products; };
Zwei Details sind wichtig. .text() gibt den kombinierten Text eines Elements zurück, daher entfernt .trim() den Leerraum, den das Markup drum herum hinterlässt. Zum Lesen eines Links verwendet man .attr("href") statt .text(), weil der gewünschte Wert im Attribut liegt, nicht im sichtbaren Text. Passen Sie die Selektoren (.product-card, h2.title, .price) an die tatsächlich angesteuerte Seite an; untersuchen Sie sie in den Entwicklertools Ihres Browsers, um die richtigen zu finden.
Klassennamen ändern sich, wenn eine Site ein Redesign veröffentlicht, und ein Selektor, der letzten Monat funktioniert hat, kann still leere Strings zurückgeben. Behandeln Sie Selektoren als etwas, das Sie pflegen, nicht einmalig setzen. Wenn ein Feld leer zurückkommt, untersuchen Sie die Live-Seite erneut und aktualisieren ihn. Regelmäßige Selektor-Pflege ist normal für jeden Produktions-Scraper.
Schritt 3: Über Listenseiten schleifen
Eine Seite ist eine Demo. Ein echter Auftrag durchläuft die Paginierung. Die meisten paginierten Listings exponieren eine Seitenzahl in der URL (?page=2) oder einen "Weiter"-Link. Der einfachste robuste Ansatz ist, einen bekannten Seitenbereich zu iterieren, jeden abzurufen, zu parsen und zu stoppen, wenn eine Seite keine Elemente zurückgibt.
const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const scrapeAll = async (baseUrl, maxPages = 10) => { const all = []; for (let page = 1; page <= maxPages; page++) { try { const html = await fetchPage(`${baseUrl}?page=${page}`); const rows = parseProducts(html); if (rows.length === 0) break; all.push(...rows); console.log(`Page ${page}: ${rows.length} items`); } catch (err) { console.error(`Page ${page} failed: ${err.message}`); } await sleep(1000); } return all; };
Das sleep zwischen Anfragen ist keine Dekoration. Eine Pause von einer Sekunde verhindert, dass Sie den Server in einer engen Schleife bombardieren, was sowohl rücksichtsvoll ist als auch die schnellste Methode, Drosselung zu vermeiden. Das try/catch bedeutet, dass eine schlechte Seite einen Fehler protokolliert und der Lauf fortsetzt, statt bei Element 4 von 200 abzubrechen.
Schritt 4: In JSON und CSV schreiben
Gesammelte Daten sind erst nützlich, wenn sie den Speicher verlassen. JSON ist der abhängigkeitsfreie Standard; Node.js' eingebautes fs-Modul schreibt es direkt. Ein flaches CSV ist genauso leicht von Hand und öffnet sich direkt in einer Tabellenkalkulation.
import { writeFileSync } from "fs"; const saveJson = (rows, file) => writeFileSync(file, JSON.stringify(rows, null, 2)); const saveCsv = (rows, file) => { const headers = Object.keys(rows[0]); const escape = (v) => `"${String(v ?? "").replace(/"/g, '""')}"`; const lines = [ headers.join(","), ...rows.map((r) => headers.map((h) => escape(r[h])).join(",")), ]; writeFileSync(file, lines.join("\n")); };
Der escape-Helfer umhüllt jeden Wert in Anführungszeichen und verdoppelt interne Anführungszeichen, was die CSV-Regel ist, die verhindert, dass ein Komma oder Anführungszeichen in einem Produkttitel Ihre Spalten verschiebt. Für aufwändigere Fälle (verschachtelte Daten, große Volumina) greifen Sie zu einer Bibliothek wie csv-stringify, aber für ein flaches Datensatz-Set reicht das aus.
Alles zusammensetzen
Verdrahten Sie die vier Teile zu einem ausführbaren Skript.
const main = async () => { const rows = await scrapeAll("https://example.com/products"); if (rows.length === 0) { console.log("No data collected."); return; } saveJson(rows, "products.json"); saveCsv(rows, "products.csv"); console.log(`Saved ${rows.length} items.`); }; main();
Führen Sie es mit node scraper.js aus. Sie erhalten eine Fortschrittszeile pro Seite und zwei Dateien auf der Festplatte. Das ist ein vollständiger statischer Scraper in deutlich unter hundert Zeilen.
Wo statisches HTTP an seine Grenzen stößt
Der axios-plus-cheerio-Stack ist schnell und sauber, und für serverseitig gerenderte Seiten ist er alles, was Sie brauchen. Aber zwei Wände tauchen schnell bei echten Zielen auf.
JavaScript-gerenderter Inhalt. Viele moderne Sites senden eine fast leere HTML-Hülle und bauen die Seite im Browser mit JavaScript auf. axios ruft nur diese initiale Hülle ab; es führt keine Skripte aus, sodass cheerio nichts findet, wo die Daten sein sollten. Wenn Ihre Selektoren bei einer Seite, die im Browser deutlich Inhalt zeigt, leere Ergebnisse zurückgeben, liegt es fast immer daran.
Blockieren bei Skalierung. Ein paar Anfragen von Ihrer IP sind in Ordnung. Ein paar Hundert von derselben Rechenzentrumsadresse, in erkennbaren Mustern, führt zu Ratenlimitierung, CAPTCHA-Mauern oder vollständiger Sperrung. Ein benutzerdefinierter User-Agent verschafft etwas Spielraum; er löst das IP-Problem nicht.
Sie haben zwei Wege nach vorne. Der erste ist ein Headless-Browser: Puppeteer oder Playwright steuert ein echtes Chrome oder Firefox, führt das JavaScript der Seite aus und ermöglicht Ihnen, das gerenderte DOM zu scrapen. Das löst das Rendering, ist aber schwer: Jede Instanz ist ein vollständiger Browser, der Arbeitsspeicher und CPU frisst, und bei Skalierung müssen Sie immer noch selbst einen Proxy-Pool verwalten, um entsperrt zu bleiben. Wenn das der gewünschte Weg ist, lesen Sie unsere Anleitung zu Playwright-Web-Scraping.
Der zweite Weg ist, beide Probleme an eine API auszulagern.
Die Crawling API für gerenderte, unblockierte Seiten verwenden
Die Crawling API faltet Rendering und IP-Rotation in eine einzige Anfrage. Sie senden ihr eine URL, sie ruft die Seite hinter einer vertrauenswürdigen rotierenden Residential-IP ab (optional zuerst JavaScript rendernd) und gibt fertiges HTML zurück. Ihr bestehender cheerio-Parser bleibt unverändert; nur der Fetch-Schritt tauscht aus.
Installieren Sie den offiziellen Node-Client.
npm install crawlbase
Ersetzen Sie dann fetchPage durch eine Version, die über die API geht. Alles downstream (parsen, schleifen, speichern) bleibt genau so, wie Sie es geschrieben haben.
import { CrawlingAPI } from "crawlbase"; import * as cheerio from "cheerio"; const api = new CrawlingAPI({ token: "YOUR_CRAWLBASE_TOKEN" }); const fetchPage = async (url) => { const response = await api.get(url, { ajax_wait: true, page_wait: 3000 }); if (response.statusCode === 200 && response.pcStatus === 200) { return response.body; } throw new Error(`Crawl failed: ${response.statusCode} / ${response.pcStatus}`); };
Zwei Dinge zu beachten. Der Client gibt sowohl statusCode (die Antwort der Zielsite) als auch pcStatus (ob der Crawl selbst erfolgreich war) zurück; beide zu prüfen verhindert, dass ein weicher Fehler als gutes HTML durchgeht. Die Optionen ajax_wait und page_wait behandeln JavaScript-gerenderte Ziele: ajax_wait weist die API an, auf asynchronen Inhalt zu warten, und page_wait hält einige Sekunden nach dem Laden inne, damit späte Elemente vor der Aufnahme erscheinen. Lassen Sie beide Optionen für einfache statische Seiten weg und Sie erhalten denselben Rotationsvorteil ohne den Rendering-Overhead.
Crawlbase-Token gibt es in zwei Varianten. Das normale Token ruft statisches HTML ab und ist die richtige Wahl für serverseitig gerenderte Seiten. Das JavaScript-(JS-)Token rendert die Seite zuerst in einem echten Browser, was für clientseitig gerenderte Ziele benötigt wird. Wenn eine Seite mit dem normalen Token als leere Hülle zurückkommt, wechseln Sie zum JS-Token.
Sparen Sie sich den Betrieb einer Headless-Browser-Flotte und die Verwaltung Ihres eigenen Proxy-Pools. Die Crawling API rendert JavaScript wenn nötig, rotiert serverseitig durch Residential-IPs und gibt in einem Aufruf fertiges HTML zurück, sodass Ihr cheerio-Parser unverändert weiterfunktioniert. Starten Sie im kostenlosen Tarif und testen Sie damit die Seiten, die Sie blockiert haben.
Tipps für einen gesunden Scraper
Einige Gewohnheiten halten einen Node.js-Scraper reibungslos am Laufen, ob Sie bei axios bleiben oder zur API wechseln.
- Nutzungsbedingungen und robots.txt der Site zuerst lesen. Wissen Sie, was Sie sammeln dürfen und in welchem Tempo, bevor Sie eine Schleife darauf richten.
- Anfragen dosieren. Eine Verzögerung zwischen Aufrufen und vernünftige Parallelität verhindern, einen Server zu überlasten, und sehen wie ein Angriff aus.
-
Realistische Header senden. Ein browserähnlicher
User-Agentund Standard-Accept-Header reduzieren die Chance, als Bot markiert zu werden. - Fehler pro Element behandeln. Jeden Fetch einwickeln, damit ein Fehler protokolliert und weitergemacht wird, statt den gesamten Lauf abzubrechen.
- Während der Entwicklung cachen. Abgerufenes HTML auf die Festplatte speichern, während Sie Selektoren iterieren, damit Sie bei jeder Code-Änderung nicht die Site erneut anschlagen.
- Status-Codes beobachten. Eine steigende Rate von Challenges oder 4xx-Antworten ist ein Signal zum Verlangsamen oder IP-Rotieren, kein Rauschen zum Ignorieren.
Das vollständige Anti-Block-Playbook, einschließlich IP-Rotation und Fingerprinting, finden Sie in Websites scrapen ohne geblockt zu werden. Wenn Sie lieber Ihren eigenen Traffic durch einen rotierenden Pool leiten statt die verwaltete API zu nutzen, gibt Ihnen der Smart AI Proxy (auch AI Proxy genannt) Residential-IP-Rotation als Drop-in-Proxy-Endpunkt.
Wichtigste Erkenntnisse
- axios plus cheerio ist der statische Stack. HTML mit axios abrufen, mit cheerios jQuery-artigen Selektoren parsen, und Sie haben einen funktionierenden Scraper in unter hundert Zeilen.
- Das Muster ist abrufen, selektieren, schleifen, speichern. Den sich wiederholenden Container finden, Selektoren auf Felder abbilden, Paginierung mit Pause durchlaufen und in JSON und CSV schreiben.
- Statisches HTTP hat zwei Grenzen. Es kann kein JavaScript ausführen, verpassen also clientseitig gerenderten Inhalt, und eine einzelne IP wird bei Skalierung geblockt.
- Puppeteer und Playwright lösen das Rendering, sind aber schwer. Ein echter Browser pro Instanz kostet Arbeitsspeicher und CPU, und Sie verwalten trotzdem selbst Proxies.
- Die Crawling API faltet beides ein. Ein Aufruf gibt gerendertes HTML hinter einer rotierenden Residential-IP zurück, und Ihr cheerio-Parser bleibt unverändert.
Häufig gestellte Fragen
Ist Node.js gut zum Web-Scraping?
Ja. Node.js führt JavaScript auf der schnellen V8-Engine aus, und sein nicht-blockierendes I/O-Modell ermöglicht es, viele Netzwerkanfragen auf einem einzigen Thread in der Luft zu halten, was genau das ist, was Scraping braucht. Das npm-Ökosystem gibt Ihnen außerdem ausgereifte Bibliotheken für jeden Schritt, von HTTP-Anfragen über HTML-Parsing bis zu Headless-Browsern, sodass die meisten Aufgaben schnell zusammenkommen.
Was ist der Unterschied zwischen axios und cheerio?
Sie erledigen zwei verschiedene Hälften der Aufgabe. axios ist ein HTTP-Client: Er ruft das rohe HTML einer Seite über das Netzwerk ab. cheerio ist ein Parser: Er lädt diesen HTML-String und gibt Ihnen jQuery-artige CSS-Selektoren, um die gewünschten Felder herauszuziehen. Fast immer verwendet man sie zusammen, axios zum Herunterladen und cheerio zum Extrahieren.
Warum gibt cheerio bei manchen Seiten leere Ergebnisse zurück?
Meistens weil die Seite ihren Inhalt clientseitig mit JavaScript rendert. axios ruft nur die initiale HTML-Hülle ab, und cheerio parst das, was ihm gegeben wird, sodass es nichts zu finden gibt, wenn die Daten erst nach dem Laden von Skripten eingefügt werden. Die Lösung ist, die Seite zuerst zu rendern, entweder mit einem Headless-Browser wie Puppeteer oder Playwright, oder mit der Crawling API über deren JavaScript-Rendering-Optionen.
Wie vermeide ich es, beim Scraping mit Node.js geblockt zu werden?
Dosieren Sie Anfragen mit einer Pause, senden Sie realistische Browser-Header, halten Sie die Parallelität vernünftig und rotieren Sie IP-Adressen, damit keine einzelne eine Ratenlimitierung auslöst. Ein benutzerdefinierter User-Agent hilft, löst das IP-Problem allein aber nicht. Rotierende Residential-Proxies oder ein verwalteter Dienst wie die Crawling API übernehmen die Rotation, sodass Sie keinen Proxy-Pool pflegen müssen.
Soll ich Puppeteer oder die Crawling API verwenden?
Verwenden Sie Puppeteer (oder Playwright), wenn Sie detaillierte Kontrolle über einen echten Browser benötigen, etwa beim Klicken durch mehrstufige Abläufe oder beim Aufnehmen von Screenshots, und bereit sind, die Browser selbst zu betreiben und zu skalieren. Verwenden Sie die Crawling API, wenn Sie hauptsächlich gerendertes, unblockiertes HTML bei Skalierung benötigen, ohne eine Headless-Flotte und einen Proxy-Pool zu verwalten. Viele Teams prototypisieren mit einem Headless-Browser und wechseln zur API, sobald Volumen und Block-Raten steigen.
Kann ich gescrapte Daten in eine Datenbank statt in Dateien schreiben?
Ja. Die gesammelten Datensätze sind einfache JavaScript-Objekte, sodass Sie das Array nach dem Sammeln überall einfügen können: in eine JSON-Datei, eine CSV oder eine Datenbank wie PostgreSQL, MongoDB oder SQLite über deren Node.js-Treiber. Der Speicherschritt ist unabhängig von der Scraping-Logik, also tauschen Sie saveJson gegen einen Insert-Aufruf aus, ohne den Fetch- oder Parse-Code anzufassen.
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.

