Zum Inhalt springen
Zurück zum Blog

Cheerio Web Scraping in Node.js: load() für einen getesteten Scraper

Raluca PenciucZuletzt aktualisiert am 18 min read
Cheerio Web Scraping in Node.js: load() für einen getesteten Scraper
Kurzfassung: Cheerio ist ein schneller HTML-Parser für Node.js mit einer API im jQuery-Stil und eignet sich daher für Web-Scraping, wenn die Daten bereits im rohen HTML der Seite stehen. Nutzen Sie fetch plus Cheerio für statische Seiten, rufen Sie das JSON der Seite direkt ab, wenn sie ihre Daten so lädt, und rendern Sie nur dann mit einem Browser, wenn JavaScript den Inhalt aufbaut. Dieser Leitfaden baut einen getesteten Scraper mit Paginierung, Wiederholungen und JSON- oder CSV-Export.

Am Ende dieser Anleitung verfügen Sie über einen Cheerio-Scraper, der eine Seite lädt, Eingabefelder extrahiert, der Paginierung folgt, bei Serverfehlern höflich erneut versucht, die Ergebnisse als JSON und CSV speichert und einen Offline-Test besteht. Die Live-Beispiele verwenden books.toscrape.com und quotes.toscrape.com, zwei Übungsseiten, die speziell für das Scraping erstellt wurden, sodass du den Code unverändert ausführen kannst. Außerdem lernst du, welche Seiten Cheerio nicht lesen kann, und wie du die „cheerio.load“-Fehler behebst, an denen die meisten Nutzer scheitern. Bist du neu im Bereich Web-Scraping mit Node? Beginne mit unserem Leitfaden zum Web-Scraping mit JavaScript und Node.js.

Was ist Cheerio und wann solltest du es für das Web-Scraping verwenden?

Cheerio ist ein schneller HTML- und XML-Parser für Node.js mit einer API im jQuery-Stil. Es liest bereits vorhandenes Markup aus und ermöglicht es dir, Elemente mit CSS-Selektoren auszuwählen. Es führt kein JavaScript aus, lädt kein CSS und rendert keine Seiten – nutze es also, wenn die benötigten Daten bereits im rohen HTML der Seite enthalten sind.

Stell dir Cheerio als das Gegenteil eines Browsers vor. Du gibst ihm eine Markup-Zeichenkette, und es erstellt einen Baum aus Elementen, die du abfragen kannst. Nichts wird ausgeführt: kein Layout, keine Bild- oder Stylesheet-Anfragen, keine Skripte, die später Inhalte hinzufügen. Deshalb ist Cheerio schnell und schlank – und deshalb findet es manchmal nichts auf Seiten, die in Chrome voller Daten zu sein scheinen.

Im Hintergrund analysiert das Cheerio-Projekt auf GitHub HTML mit „parse5“ und kann auf den fehlertoleranteren „htmlparser2“ umschalten. Darüber liegt die vertraute API im jQuery-Stil: $('selector'), .find(), .text(), und .attr(). Einen Vergleich mit anderen Tools findest du in unserer Übersicht über Node.js-Web-Scraping-Bibliotheken.

Entscheiden Sie zunächst, um welche Art von Seite es sich handelt:

Seitentyp

Was Sie verwenden sollten

Warum

Daten im rohen HTML-Code

fetch + Cheerio

Am schnellsten und kostengünstigsten. Es wird nichts ausgeführt

Daten im JSON-Format, das die Seite anfordert

Rufe das JSON direkt auf

Kein Parsen des Markups

Inhalt wird durch JavaScript gerendert oder gesperrt

Gerendertes HTML (Scraper-API render_js oder Browser-API), anschließend Cheerio

Cheerio kann keine Skripte ausführen

Sobald Sie wissen, dass Cheerio die Seite lesen kann, extrahieren Sie in dieser Reihenfolge:

  • Suchen Sie zunächst nach strukturierten Daten, wie z. B. JSON-LD-Blöcken oder einem Datenobjekt innerhalb eines Script-Tags.
  • Greife auf CSS-Selektoren im sichtbaren Markup zurück, wenn keine strukturierten Daten vorhanden sind.
  • Greifen Sie erst dann auf das Rendering zurück, wenn die Daten überhaupt nicht im vom Server gesendeten HTML vorkommen.

Wenn Sie einen Parser mit einem vollständigen Browser vergleichen möchten, finden Sie in unserem Vergleich „Cheerio vs. Puppeteer“ eine Übersicht über die Vor- und Nachteile.

Wie richtet man ein Cheerio-Projekt in Node.js ein?

Installieren Sie eine aktuelle Node.js-LTS-Version, erstellen Sie ein Projekt mit `npm init`, fügen Sie „type": „module“ zu `package.json` hinzu und führen Sie `npm install cheerio` aus. Importiere es mit `import * as cheerio from 'cheerio'`. Die in Node integrierte `fetch`-Funktion lädt Seiten herunter, sodass du weder Axios noch einen anderen HTTP-Client benötigst, um loszulegen.

Verwenden Sie Node.js 22 oder höher; die aktuelle LTS-Version ist die beste Wahl. Überprüfen Sie Ihre Version und erstellen Sie dann das Projekt:

node -v
mkdir cheerio-scraper && cd cheerio-scraper
npm init -y
npm pkg set type=module
npm install cheerio

Die .mjs Erweiterung weist Node an, jede Datei als ES-Modul zu behandeln. In einem CommonJS-Projekt const cheerio = require('cheerio') funktioniert das ebenfalls. Die Installationsanleitung von Cheerio beschreibt dieselben Schritte.

Das in Node integrierte `fetch` übernimmt die Downloads. Es basiert auf `undici`, muss nicht importiert werden und unterstützt AbortSignal.timeout().

Verwenden Sie bereits Axios? Behalten Sie es bei. Übergeben Sie response.data an cheerio.load() anstelle von await res.text(), und der Rest dieser Anleitung gilt unverändert. Hier erfährst du, wie du in Axios Header für das Scraping setzt, oder vergleiche den nativen `fetch`-Aufruf mit seinen Alternativen.

Verwendung von Cheerio mit TypeScript

Cheerio enthält eigene Typdefinitionen, daher muss kein @types/cheerio Paket installiert werden muss. Importieren Sie load und den CheerioAPI Typ ein, versehen Sie Ihre Rückgabetypen mit Annotationen, und der Compiler überprüft Ihren Parser wie jeden anderen Code:

// TypeScript: Cheerio ships its own types, so no @types package is needed.
import { load, type CheerioAPI } from 'cheerio';

interface Book {
  title: string;
  price: number;
}

export function parseBooks($: CheerioAPI): Book[] {
  return $('article.product_pod')
    .map((_, el) => ({
      title: $(el).find('h3 a').attr('title') ?? '',
      price: Number($(el).find('.price_color').text().replace(/[^0-9.]/g, '')),
    }))
    .get();
}

const $ = load('<article class="product_pod"><h3><a title="Sample">S</a></h3><p class="price_color">£9.99</p></article>');
console.log(parseBooks($));

Führen Sie es aus mit npx tsx 09-typed.ts:

[ { title: 'Sample', price: 9.99 } ]

Kompilieren mit tsc stattdessen? Fügen Sie @types/node als Entwicklungsabhängigkeit hinzu, da die Typdefinitionen von Cheerio auf Node-Built-ins wie Buffer und node:stream. Die Parsing-Logik ist identisch mit der JavaScript-Version.

Wie funktioniert `cheerio.load()`?

cheerio.load() parst eine HTML-Zeichenkette und gibt eine $-Funktion zurück. Rufe $ mit einem CSS-Selektor auf, um passende Elemente zu erhalten, und lies diese dann mit .text() oder .attr() aus. Verwende .find(), um innerhalb eines Elements zu suchen, .map() mit .get(), um Arrays zu erstellen, und .next() oder .parent(), um dich im Dokumentbaum zu bewegen.

Beginnen Sie mit einer kleinen statischen Seite, bevor Sie sich mit dem Netzwerk befassen. Sie deckt alle Methoden ab, die im weiteren Verlauf des Leitfadens verwendet werden:

// Cheerio basics: load HTML, select elements, read text and attributes.
import * as cheerio from 'cheerio';

const html = `
<ul id="products">
  <li class="product" data-sku="A1"><a href="/p/a1">Desk lamp</a><span class="price">$24.00</span></li>
  <li class="product" data-sku="B2"><a href="/p/b2">Monitor arm</a><span class="price">$61.50</span></li>
  <li class="product sold-out" data-sku="C3"><a href="/p/c3">Cable tray</a><span class="price">$12.99</span></li>
</ul>`;

const $ = cheerio.load(html);

// One element: .text() and .attr()
console.log('First product:', $('li.product a').first().text());
console.log('First link:', $('li.product a').first().attr('href'));

// Many elements: .map() returns a Cheerio object, .get() turns it into an array
const products = $('li.product')
  .map((_, el) => {
    const item = $(el);
    return {
      sku: item.attr('data-sku'),
      name: item.find('a').text().trim(),
      price: Number(item.find('.price').text().replace(/[^0-9.]/g, '')),
      inStock: !item.hasClass('sold-out'),
    };
  })
  .get();
console.log(products);

// Traversal: move from one element to its neighbors
console.log('After A1:', $('li[data-sku="A1"]').next().find('a').text());
console.log('Parent id:', $('.price').first().parent().parent().attr('id'));

Die Ausgabe von node 01-basics.mjs:

First product: Desk lamp
First link: /p/a1
[
  { sku: 'A1', name: 'Desk lamp', price: 24, inStock: true },
  { sku: 'B2', name: 'Monitor arm', price: 61.5, inStock: true },
  { sku: 'C3', name: 'Cable tray', price: 12.99, inStock: false }
]
After A1: Monitor arm
Parent id: products

Auswahl von Elementen

Die Zeichenkette, die Sie an $() , ist ein Standard-CSS-Selektor; daher sind Tag-Namen, .class, #id, Attributselektoren wie [data-sku="A1"]sowie Kombinatoren wie ul > li funktionieren alle. Der Leitfaden von MDN zu CSS-Selektoren ist die beste Referenz. Cheerio fügt einige Erweiterungen im jQuery-Stil hinzu, wie zum Beispiel :contains() und :first, unterstützt jedoch kein XPath; hier erfährst du, wann welche Selektorsprache am besten geeignet ist.

Text und Attribute auslesen

.text() gibt den kombinierten Text jedes übereinstimmenden Elements und seiner Nachkommen zurück. .attr('name') Gibt ein Attribut des ersten gefundenen Elements zurück, genau so, wie es im Markup geschrieben steht. .hasClass() prüft auf eine Klasse – praktisch für Markierungen wie „ausverkauft“.

.map() ist der Schritt, der die meisten Neulinge verwirrt. In Cheerio gibt er ein weiteres Cheerio-Objekt statt eines einfachen Arrays zurück, daher console.log() wird ein Wrapper voller interner Daten ausgegeben. Fügen Sie .get() am Ende hinzu, um das erwartete Array aus einfachen Objekten zu erhalten.

Sich im Baum bewegen

Wenn das gewünschte Element selbst keinen brauchbaren Selektor hat, beginne bei einem, der einen hat, und gehe dorthin. .find() durchsucht die Nachkommen, .parent() bewegt sich eine Ebene nach oben, .next() springt zum nächsten Geschwisterelement und .first() behält nur die erste Übereinstimmung bei. Die letzten beiden Zeilen des Beispiels springen von einem Produkt zum nächsten und von einem Preis zu dessen Liste.

Wie scrapt man eine echte Webseite mit Cheerio?

Lade die Seite mit `fetch` herunter, überprüfe den Antwortstatus und übergebe den HTML-Text an `cheerio.load()`. Durchlaufe dann jede sich wiederholende Karte mit `.map()`, lese alle benötigten Felder aus, wandle Preise in Zahlen um und löse relative Links mit `new URL()` auf, damit die Ausgabe saubere, typisierte Daten statt roher Zeichenfolgen enthält.

„Books to Scrape“ listet 20 Bücher pro Seite auf, jedes in einer article.product_pod Karte. Dieses Skript ruft die erste Seite ab und wandelt jede Karte in einen typisierten Datensatz um:

// Fetch one page with Node's built-in fetch and parse it with Cheerio.
import * as cheerio from 'cheerio';

const START_URL = 'https://books.toscrape.com/';
const RATINGS = { One: 1, Two: 2, Three: 3, Four: 4, Five: 5 };

async function getHtml(url) {
  const res = await fetch(url, {
    headers: { 'User-Agent': 'cheerio-tutorial/1.0 (+https://www.webscrapingapi.com)' },
    signal: AbortSignal.timeout(15_000),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
  return res.text();
}

const html = await getHtml(START_URL);
const $ = cheerio.load(html);

const books = $('article.product_pod')
  .map((_, el) => {
    const card = $(el);
    const link = card.find('h3 a');
    const ratingWord = card.find('p.star-rating').attr('class').replace('star-rating', '').trim();
    return {
      title: link.attr('title'),
      price: Number(card.find('.price_color').text().replace(/[^0-9.]/g, '')),
      rating: RATINGS[ratingWord] ?? null,
      inStock: card.find('.availability').text().includes('In stock'),
      url: new URL(link.attr('href'), START_URL).href,
    };
  })
  .get();

console.log(`Found ${books.length} books`);
console.log(books.slice(0, 3));

Hier sind die ersten drei Datensätze aus node 02-books-page.mjs:

Found 20 books
[
  {
    title: 'A Light in the Attic',
    price: 51.77,
    rating: 3,
    inStock: true,
    url: 'https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html'
  },
  {
    title: 'Tipping the Velvet',
    price: 53.74,
    rating: 1,
    inStock: true,
    url: 'https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html'
  },
  {
    title: 'Soumission',
    price: 50.1,
    rating: 1,
    inStock: true,
    url: 'https://books.toscrape.com/catalogue/soumission_998/index.html'
  }
]

Vier kleine Tricks verwandeln rohe Zeichenfolgen in verwertbare Daten:

  • Preis: Auf der Seite wird „£51,77“ angezeigt. Entferne alles außer den Ziffern und dem Dezimalpunkt, rufe Number(), und man erhält 51,77, das man sortieren und summieren kann.
  • Bewertung: Die Sterne bestehen ausschließlich aus CSS. Das einzige Signal im HTML-Code ist eine Klasse wie star-rating Three, daher entfernt das Skript star-rating und ordnet das verbleibende Wort einer Zahl zu.
  • Lagerbestand: „Auf Lager“ wird zu einem booleschen Wert, der sich leichter filtern lässt als Freitext.
  • URL: Die href Werte sind relativ, wie catalogue/a-light-in-the-attic_1000/index.html. Der URL-Konstruktor wandelt sie anhand der Seitenadresse in Links um, die überall funktionieren.

Wähle Selektoren aus der Quellansicht aus, nicht aus dem „Elemente“-Panel der DevTools. Die DevTools zeigen das DOM an, nachdem Skripte ausgeführt wurden, während Cheerio nur den Rohquellcode sieht, den der Server gesendet hat. Ein Selektor, der in den DevTools funktioniert, aber nicht in der Quellansicht, wird in deinem Skript keine Übereinstimmung finden.

Der Fetch-Aufruf legt außerdem ein Timeout fest, sodass eine ins Stocken geratene Antwort den Lauf nicht zum Stillstand bringen kann, sowie einen ehrlichen User-Agent, der Ihr Tool benennt. Ohne einen solchen identifiziert sich Node als bloßer „node“, wie httpbin zeigt:

// See which User-Agent your scraper sends, then set an honest one.
const plain = await fetch('https://httpbin.org/user-agent').then((r) => r.json());
console.log('default:', plain['user-agent']);

const named = await fetch('https://httpbin.org/user-agent', {
  headers: { 'User-Agent': 'cheerio-tutorial/1.0 (+https://www.webscrapingapi.com)' },
}).then((r) => r.json());
console.log('custom:', named['user-agent']);
default: node
custom: cheerio-tutorial/1.0 (+https://www.webscrapingapi.com)

Möchtest du Zeilen und Spalten statt Karten scrapen? Folge unserem Tutorial zum Scrapen von HTML-Tabellen in JavaScript.

Wie scrapt man mehrere Seiten mit Cheerio?

Scrape die erste Seite, lies den href des „Weiter“-Links, setze ihn in die aktuelle URL um und wiederhole dies, bis kein „Weiter“-Link mehr vorhanden ist oder du an eine Seitenbegrenzung stößt. Warte zwischen den Anfragen, versuche es nur bei 429- und 5xx-Antworten mit zunehmenden Verzögerungen erneut und schreibe die Ergebnisse am Ende in JSON oder CSV.

Dieser Crawler baut auf dem vorherigen Skript auf. Er folgt dem „Weiter“-Button durch den Katalog, versucht vorübergehende Fehler erneut und speichert die Ergebnisse in zwei Dateien:

// Follow "next" links, retry politely, and save results to JSON and CSV.
import * as cheerio from 'cheerio';
import { writeFile } from 'node:fs/promises';
import { setTimeout as sleep } from 'node:timers/promises';

const START_URL = 'https://books.toscrape.com/catalogue/page-1.html';
const MAX_PAGES = Number(process.argv[2] ?? 3);
const DELAY_MS = 1_000;
const RATINGS = { One: 1, Two: 2, Three: 3, Four: 4, Five: 5 };

async function getHtml(url, attempts = 3) {
  for (let attempt = 1; attempt <= attempts; attempt++) {
    let waitMs = DELAY_MS * 2 ** attempt; // 2 s, then 4 s
    try {
      const res = await fetch(url, {
        headers: { 'User-Agent': 'cheerio-tutorial/1.0 (+https://www.webscrapingapi.com)' },
        signal: AbortSignal.timeout(15_000),
      });
      if (res.ok) return res.text();
      // Retry only on rate limits and server errors
      if (res.status !== 429 && res.status < 500) throw new Error(`HTTP ${res.status} for ${url}`);
      // Honor Retry-After when the server sends it in seconds
      const retryAfter = Number(res.headers.get('retry-after'));
      if (retryAfter > 0) waitMs = retryAfter * 1_000;
      console.warn(`HTTP ${res.status} on attempt ${attempt} for ${url}`);
    } catch (err) {
      if (attempt === attempts || err.message.startsWith('HTTP 4')) throw err;
      console.warn(`${err.name} on attempt ${attempt} for ${url}`);
    }
    if (attempt < attempts) await sleep(waitMs);
  }
  throw new Error(`Gave up on ${url}`);
}

function parseBooks($, pageUrl) {
  return $('article.product_pod')
    .map((_, el) => {
      const card = $(el);
      const link = card.find('h3 a');
      const ratingWord = card.find('p.star-rating').attr('class').replace('star-rating', '').trim();
      return {
        title: link.attr('title'),
        price: Number(card.find('.price_color').text().replace(/[^0-9.]/g, '')),
        rating: RATINGS[ratingWord] ?? null,
        inStock: card.find('.availability').text().includes('In stock'),
        url: new URL(link.attr('href'), pageUrl).href,
      };
    })
    .get();
}

function toCsv(rows) {
  const cols = Object.keys(rows[0]);
  const escape = (v) => {
    const s = String(v ?? '');
    return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
  };
  return [cols.join(','), ...rows.map((r) => cols.map((c) => escape(r[c])).join(','))].join('\n');
}

const books = [];
let url = START_URL;
for (let page = 1; url && page <= MAX_PAGES; page++) {
  const $ = cheerio.load(await getHtml(url));
  const found = parseBooks($, url);
  books.push(...found);
  console.log(`Page ${page}: ${found.length} books`);

  const next = $('li.next a').attr('href');
  url = next ? new URL(next, url).href : null;
  if (url) await sleep(DELAY_MS);
}

await writeFile('out/books.json', JSON.stringify(books, null, 2));
await writeFile('out/books.csv', toCsv(books));
console.log(`Saved ${books.length} books to out/books.json and out/books.csv`);

Erstellen Sie den Ausgabeordner und führen Sie das Skript dann mit einer Obergrenze von drei Seiten aus:

mkdir out
node 03-crawl-books.mjs 3
Page 1: 20 books
Page 2: 20 books
Page 3: 20 books
Saved 60 books to out/books.json and out/books.csv

Die ersten Zeilen von out/books.csv:

title,price,rating,inStock,url
A Light in the Attic,51.77,3,true,https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html

Drei Sicherheitsvorkehrungen sorgen dafür, dass der Crawler höflich und vorhersehbar bleibt:

  • Eine Seitenbegrenzung. Das erste Befehlszeilenargument legt MAX_PAGES. Die Schleife bricht außerdem ab, wenn eine Seite keinen „Weiter“-Link enthält.
  • Eine feste Verzögerung. Das Skript wartet eine Sekunde zwischen den Seiten.
  • Eine strenge Wiederholungsrichtlinie. Nur HTTP-Fehler 429 „Too Many Requests“ und 5xx-Serverfehler werden wiederholt – nach 2 Sekunden und dann nach 4 Sekunden oder nach dem Retry-After Wert, den der Server in Sekunden angibt. Jeder andere 4xx-Fehler, wie beispielsweise ein 404, wird sofort abgebrochen, da eine erneute Anfrage die Antwort nicht ändern wird.

Der CSV-Writer setzt jedes Feld, das ein Komma, ein doppeltes Anführungszeichen oder einen Zeilenumbruch enthält, in Anführungszeichen und verdoppelt die Anführungszeichen darin. Viele Buchtitel enthalten Kommas, und ein einziges Komma ohne Anführungszeichen würde alle nachfolgenden Spalten verschieben.

Verzögerungen und Wiederholungsversuche sind das Minimum. Unser Leitfaden zu Best Practices beim Web-Scraping behandelt Ratenbegrenzungen, Zeitplanung und Überwachung.

Wie verwendet man die Funktionen `fromURL()` und `extract()` von Cheerio?

fromURL() lädt eine Seite herunter und lädt sie in einem einzigen, abgewarteten Aufruf. extract() ordnet dann ein einfaches Objekt aus Selektoren den Werten zu: Eine Zeichenkette gibt den Text der ersten Übereinstimmung zurück, ein Array gibt alle Übereinstimmungen zurück, und ein verschachteltes Objekt oder eine Funktion erstellt strukturierte Datensätze. Zusammen ersetzen sie die meisten handgeschriebenen .map()-Schleifen für einfache Seiten.

Hier ist dasselbe Konzept bei „Quotes to Scrape“, ohne Abrufaufruf und ohne manuelle Schleife:

// Cheerio's fromURL() and extract(): fetch and map fields in one step.
import { fromURL } from 'cheerio';

const PAGE_URL = 'https://quotes.toscrape.com/';
const $ = await fromURL(PAGE_URL);

const data = $.extract({
  pageTitle: 'title',
  quotes: [
    {
      selector: 'div.quote',
      value: {
        text: 'span.text',
        author: 'small.author',
        authorUrl: {
          selector: 'a[href^="/author/"]',
          value: (el) => new URL($(el).attr('href'), PAGE_URL).href, // resolve relative links
        },
        tags: { selector: '.tags', value: (el) => $(el).find('a.tag').map((_, a) => $(a).text()).get() },
      },
    },
  ],
});

console.log(data.pageTitle.trim());
console.log(`Quotes on page: ${data.quotes.length}`);
console.log(data.quotes[0]);
Quotes to Scrape
Quotes on page: 10
{
  text: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”',
  author: 'Albert Einstein',
  authorUrl: 'https://quotes.toscrape.com/author/Albert-Einstein',
  tags: [ 'change', 'deep-thoughts', 'thinking', 'world' ]
}

Jeder Schlüssel beschreibt ein Ausgabefeld. pageTitle: 'title' liest den Text des ersten <title> Element. Das Einbetten eines Deskriptors in ein Array, wie quotes es hier tut, liefert ein Ergebnis pro Treffer, und die value Eigenschaft kann ein Selektor, ein verschachteltes Objekt oder eine Funktion sein, die das Element entgegennimmt. Die „extract()“-Referenz listet alle Optionen auf.

Lösen Sie Links selbst auf. Je nach Ihrer Cheerio-Version href Werten, die über extract() oder .prop('href') können nach der Verarbeitung fromURL(), weshalb der Codeausschnitt jeden einzelnen durch new URL(). Diese kleine Absicherung sorgt dafür, dass Ihre Ausgabe in beiden Fällen korrekt ist.

Die Dokumentation von Cheerio zum Laden von Dokumenten enthält zwei weitere Details. fromURL() Es lehnt Antworten, die nicht im 2xx-Bereich liegen, mit einem Fehler ab, anstatt dir eine Fehlerseite zum Parsen zu übergeben. Und wenn du requestOptions , um Header zu setzen, musst du method selbst festlegen, da diese nicht automatisch ausgefüllt werden.

Verwenden Sie fetch plus load() , wenn du benutzerdefinierte Wiederholungsversuche, Verzögerungen oder eine Proxy-Ebene benötigst, wie im obigen Crawler.

Wie erhält man Daten aus JSON-LD- und Script-Tags?

Bevor du Selektoren schreibst, durchsuche den HTML-Code nach „script type=“application/ld+json““-Blöcken und Inline-Skripten, die Daten enthalten. Wähle sie mit Cheerio aus, lies ihren Text aus und parse ihn mit JSON.parse innerhalb eines try/catch-Blocks. Strukturierte Daten überstehen Neugestaltungen in der Regel besser als sichtbares Markup und enthalten oft saubere Preise, Artikelnummern und Verfügbarkeitsangaben.

Viele Shop- und Artikelseiten betten schema.org-Daten für Suchmaschinen ein, und wenn diese vorhanden sind, stellen sie oft die sauberste Quelle auf der Seite dar:

// Read structured data (JSON-LD) before writing selectors for visible text.
import { load } from 'cheerio';

const html = `<html><head>
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"Product","name":"Desk lamp","sku":"A1",
 "offers":{"@type":"Offer","price":"24.00","priceCurrency":"USD","availability":"https://schema.org/InStock"}}
</script></head><body><h1>Desk lamp</h1></body></html>`;

const $ = load(html);
const items = $('script[type="application/ld+json"]')
  .map((_, el) => {
    try {
      return JSON.parse($(el).text());
    } catch {
      return null; // skip malformed blocks instead of crashing
    }
  })
  .get()
  .filter(Boolean);

const product = items.find((item) => item['@type'] === 'Product');
console.log({ name: product.name, price: Number(product.offers.price), currency: product.offers.priceCurrency });
{ name: 'Desk lamp', price: 24, currency: 'USD' }

Der try/catch verhindert, dass ein einziger fehlerhafter Block den Lauf zum Absturz bringt. Filtern Sie nach der @type benötigten Kriterien, wie z. B. „Product“ oder „Article“, da eine Seite oft mehrere Blöcke enthält.

Manche Websites liefern ihren gesamten Datensatz in einem Inline-Skript aus und bauen das sichtbare Markup mit JavaScript auf. Dieser Fall sieht auf den ersten Blick fehlerhaft aus, daher erhält er einen eigenen Abschnitt.

Warum gibt Cheerio auf JavaScript-gerenderten Seiten keine Ergebnisse zurück?

Cheerio sieht nur das HTML, das der Server sendet. Wenn eine Seite ihren Inhalt mit JavaScript im Browser aufbaut, finden Ihre Selektoren nichts, da die Elemente noch nicht existieren. Überprüfen Sie zunächst das Roh-HTML. Suchen Sie dann nach den Daten in Skripten oder einem JSON-Endpunkt, oder rufen Sie das gerenderte HTML ab und analysieren Sie dieses mit Cheerio.

„Quotes to Scrape“ verfügt über eine JavaScript-Version unter /js/ , die im Browser identisch aussieht. Dieses Skript zeigt, was Cheerio tatsächlich erhält und wo sich die Daten verstecken:

// Spot a JavaScript-rendered page, then look for the data inside a <script> tag.
import { fromURL } from 'cheerio';

const $ = await fromURL('https://quotes.toscrape.com/js/');

// 1. The selector that works on the static version finds nothing here
console.log('div.quote elements in raw HTML:', $('div.quote').length);

// 2. The quotes still ship in the HTML, inside an inline script
const script = $('script')
  .map((_, el) => $(el).text())
  .get()
  .find((code) => code.includes('var data ='));

// Capture the array literal between "var data =" and the first "];"
const json = script.match(/var data = (\[[\s\S]*?\]);/)[1];
const quotes = JSON.parse(json);

console.log(`Quotes found in <script>: ${quotes.length}`);
console.log({ author: quotes[0].author.name, tags: quotes[0].tags });
div.quote elements in raw HTML: 0
Quotes found in <script>: 10
{
  author: 'Albert Einstein',
  tags: [ 'change', 'deep-thoughts', 'thinking', 'world' ]
}

Der Selektor, der auf der statischen Seite 10 Zitate gefunden hat, findet hier 0, da die div.quote Elemente erst im Browser erstellt werden. Die Zitate befinden sich zwar noch im HTML-Code, jedoch innerhalb einer var data = [...] Zuweisung, sodass ein regulärer Ausdruck und JSON.parse() alle 10 wiederherstellen. Das ist der Unterschied zwischen serverseitigem und clientseitigem Rendering in der Praxis.

Wenn sich die Daten nicht in einem Skript befinden, probiere diese Optionen der Reihe nach aus:

  1. Suche nach der JSON-Anfrage. Öffne in den DevTools den Reiter „Netzwerk“, filtere nach „Fetch/XHR“ und lade die Seite neu. Viele Seiten laden ihren Inhalt von einem JSON-Endpunkt. Wenn dieser öffentlich ist und du ihn nutzen darfst, rufe ihn direkt auf und überspringe das Parsen des HTML-Codes.
  2. Zuerst rendern, dann parsen. Rendern bedeutet, die Seite in einer echten Engine – in der Regel einem Headless-Browser – auszuführen, bevor du sie parsierst. Dein Cheerio-Code ändert sich nicht; nur die HTML-Quelle ändert sich.

Du kannst einen Browser selbst ausführen oder dies von einer API erledigen lassen. Die Scraper-API kann die Seite rendern und das endgültige HTML zurückgeben, das du direkt an `cheerio.load()` übergibst. Füge render_js=1 zur Anfrage hinzu, und die Antwort ist der HTML-Code, nachdem die Skripte ausgeführt wurden. Das Rendern von JavaScript und Geotargeting kosten keine zusätzlichen Credits, und fehlgeschlagene Anfragen werden nicht in Rechnung gestellt.

// When a page needs a browser, fetch rendered HTML from Scraper API and keep parsing with Cheerio.
import { load } from 'cheerio';

const apiKey = process.env.WSA_API_KEY;
if (!apiKey) {
  console.error('Set WSA_API_KEY to run this example.');
  process.exit(1);
}

const params = new URLSearchParams({
  api_key: apiKey,
  url: 'https://quotes.toscrape.com/js/',
  render_js: '1',
});

const res = await fetch(`https://api.webscrapingapi.com/v2?${params}`, {
  signal: AbortSignal.timeout(120_000),
});
if (!res.ok) throw new Error(`Scraper API returned HTTP ${res.status}`);

const $ = load(await res.text());
console.log('div.quote elements after rendering:', $('div.quote').length);
console.log('First author:', $('div.quote small.author').first().text());

Führe den Vorgang mit deinem Schlüssel in einer Umgebungsvariablen aus, damit dieser niemals in deinem Code auftaucht:

WSA_API_KEY=your_key node 08-render-handoff.mjs

Wenn die Daten erst nach Klicks, Scrollvorgängen oder einer Wartezeit erscheinen, führt die Browser-API diese Schritte für dich aus. Du beschreibst Wartezeiten und Aktionen wie Klicken, Scrollen, Tippen und Absenden in einer REST-Anfrage, der Browser läuft auf unserer Seite, und der zurückgegebene HTML-Code wird cheerio.load() wie jede andere Seite.

Warum funktioniert `cheerio.load` nicht? Häufige Fehler und Lösungen

Die meisten Probleme mit „cheerio.load“ haben wenige Ursachen: einen Standard-Import, den Cheerio 1.x nicht mehr bereitstellt, Selektoren, die auf das gerenderte DOM des Browsers abzielen, in `html`- und `body`-Tags eingeschlossene Fragmente, als HTML geparstes XML sowie relative Links oder unordentliche Leerzeichen. Jede der folgenden Lösungen ist eine einzeilige Änderung, die du in Sekundenschnelle testen kannst.

Dieses Skript zeigt fünf der sieben unten aufgeführten Lösungen vor und nach der Korrektur:

// Fixes for the errors people hit most often with cheerio.load().
import * as cheerio from 'cheerio';
import { load } from 'cheerio';

// Fix 1: there is no default export in Cheerio 1.x.
// `import cheerio from 'cheerio'` fails; use a namespace or named import.
console.log('namespace load is a function:', typeof cheerio.load === 'function');
console.log('named load is a function:', typeof load === 'function');

// Fix 2: load() wraps fragments in <html><head><body> by default.
const fragment = '<li>Apples</li><li>Pears</li>';
console.log('document mode:', load(fragment).html());
console.log('fragment mode:', load(fragment, null, false).html());

// Fix 3: parse XML (RSS, sitemaps) with the xml option so tags like <link> keep their text.
const rss = `<rss><channel><item><title>Post</title><link>https://example.com/post</link></item></channel></rss>`;
console.log('HTML mode <link>:', JSON.stringify(load(rss)('item link').text()));
console.log('XML mode <link>:', JSON.stringify(load(rss, { xml: true })('item link').text()));

// Fix 4: .text() keeps whitespace and joins nested text. Normalize it yourself.
const $ = load('<p class="desc">\n  Fast,\n  <b>small</b>   and   tested.\n</p>');
console.log('raw text:', JSON.stringify($('.desc').text()));
console.log('clean text:', JSON.stringify($('.desc').text().replace(/\s+/g, ' ').trim()));

// Fix 5: .attr('href') returns the value as written. Resolve it against the page URL.
const page = 'https://books.toscrape.com/catalogue/page-2.html';
console.log('absolute URL:', new URL('../index.html', page).href);
namespace load is a function: true
named load is a function: true
document mode: <html><head></head><body><li>Apples</li><li>Pears</li></body></html>
fragment mode: <li>Apples</li><li>Pears</li>
HTML mode <link>: ""
XML mode <link>: "https://example.com/post"
raw text: "\n  Fast,\n  small   and   tested.\n"
clean text: "Fast, small and tested."
absolute URL: https://books.toscrape.com/index.html

„cheerio.load ist keine Funktion“ oder „cheerio.default ist undefiniert“

Symptom: Ihr Skript schlägt beim ersten Cheerio-Aufruf fehl oder cheerio.default wird als undefiniert zurückgegeben. In nativen ES-Modulen import cheerio from 'cheerio' schlägt es sogar noch früher mit der Meldung „bietet keinen Export namens ‚default‘“ fehl.

Ursache: Cheerio 1.x hat keinen Standard-Export. Ältere Tutorials und Code aus der Zeit vor Version 1.0 verwendeten einen.

Behebung: Verwende einen Namespace-Import, import * as cheerio from 'cheerio'oder einen benannten Import, import { load } from 'cheerio'. In CommonJS const cheerio = require('cheerio') funktioniert es.

Ihre Selektoren liefern keine Ergebnisse

Symptom: $('...').length ist 0, und .text() gibt eine leere Zeichenkette zurück.

Ursache: Eine von drei Möglichkeiten. Sie haben den Selektor aus den DevTools kopiert, die das DOM nach Ausführung der Skripte anzeigen; der Inhalt wird per JavaScript gerendert, wie im Abschnitt über JavaScript-gerenderte Seiten beschrieben; oder der Server hat eine Sperrseite, eine Anmeldeseite oder eine Fehlermeldung zurückgegeben.

Behebung: Protokollieren Sie res.status und die ersten 200 Zeichen des HTML-Codes mit console.log(res.status, html.slice(0, 200)). Überprüfen Sie anschließend Ihren Selektor anhand der Quellcode-Ansicht und nicht im Element-Panel.

Zusätzliche HTML-, Head- und Body-Tags in der Ausgabe

Symptom: Du lädst ein Snippet wie <li>Apples</li>, und .html() es wird in <html><head></head><body>.

Ursache: Standardmäßig load() behandelt die Eingabe als vollständiges Dokument und fügt die fehlenden Umschlag-Tags hinzu, genau wie es ein Browser tun würde.

Behebung: Lade Fragmente im Fragmentmodus mit load(html, null, false). Das dritte Argument deaktiviert den Dokumentmodus.

XML- und RSS-Feeds verlieren Daten

Symptom: Du analysierst einen RSS-Feed oder eine Sitemap, und Felder wie <link> werden leer zurückgegeben.

Ursache: Im HTML-Modus <link> ein leeres Element und kann keinen Text enthalten, sodass die darin enthaltene URL verworfen wird. In der obigen Ausgabe gab der HTML-Modus "" , während der XML-Modus die URL zurückgab.

Behebung: Verwenden Sie load(xml, { xml: true }). Der XML-Modus behält zudem die Groß-/Kleinschreibung der Tag-Namen bei und fügt keine „html“- oder „body“-Wrapper hinzu.

Text wird mit zusätzlichen Leerzeichen zurückgegeben

Symptom: .text() Es werden Zeichenfolgen zurückgegeben, die voller Zeilenumbrüche und Leerzeichenfolgen sind.

Ursache: .text() Behält die Leerzeichen der Quelle bei, einschließlich Einrückungen, und verbindet verschachtelte Elemente wie <b> ohne Trennzeichen.

Behebung: Normalisieren Sie den Text mit .text().replace(/\s+/g, ' ').trim(). Wählen Sie bei einer ganzen Seite zunächst das Hauptinhaltselement aus, um Navigations- und Fußzeilentext auszuschließen.

403- oder 429-Fehler

Symptom: Die Anfrage schlägt mit dem HTTP-Fehler 403 „Forbidden“ oder 429 „Too Many Requests“ fehl, und Ihr Parser wird nie ausgeführt.

Ursache: Diese Fehler stammen vom Server, nicht von Cheerio. Cheerio sieht den HTML-Code erst, nachdem die Anfrage erfolgreich war.

Behebung: Lege einen korrekten User-Agent fest, verlangsame die Abfrage, beachte Retry-Afterund die robots.txt sowie die Nutzungsbedingungen der Website einhalten. Wenn eine Website weiterhin einfache Anfragen auf Seiten blockiert, die Sie erfassen dürfen, leiten Sie den Abruf über die Scraper-API weiter und behalten Sie Ihren Parser bei.

Symptom: .attr('href') Es werden Pfade wie ../index.html oder /author/Albert-Einstein die außerhalb der Seite nicht funktionieren.

Ursache: .attr() Gibt den Wert genau so zurück, wie er im Markup geschrieben steht.

Behebung: Lösen Sie jeden Link mit new URL(href, pageUrl).hrefund verwende dabei die Adresse der abgerufenen Seite als Basis.

Wie verhindert man, dass ein Cheerio-Scraper abstürzt?

Trenne das Parsen vom Abrufen, speichere eine echte Seite als Fixture und teste den Parser offline mit `node:test`. Bevorzuge stabile Hooks wie IDs, Datenattribute und `itemprop` gegenüber Styling-Klassen und löse einen Fehler aus, wenn eine Seite keine Datensätze liefert, damit ein Redesign deutlich auffällt, anstatt leere Dateien zu speichern.

Scraper fallen selten mit einem Fehler aus. Meistens ändert sich das Layout, alle Selektoren treffen stillschweigend auf nichts, und man findet eine Woche lang leere Dateien vor. Machen Sie den Parser zu einer reinen Funktion, die Sie testen können, und sorgen Sie dafür, dass er eine Fehlermeldung ausgibt, wenn er nichts findet.

Speichere eine Kopie einer echten Seite als Fixture:

mkdir fixtures
curl -s https://books.toscrape.com/catalogue/page-1.html -o fixtures/books-page-1.html

Verlagern Sie das Parsen in ein eigenes Modul. Es nimmt HTML und eine URL entgegen und kümmert sich nicht darum, woher das HTML stammt:

// parse-books.mjs: keep parsing separate from fetching so it can be tested offline.
import { load } from 'cheerio';

const RATINGS = { One: 1, Two: 2, Three: 3, Four: 4, Five: 5 };

export function parseBooks(html, pageUrl) {
  const $ = load(html);
  const books = $('article.product_pod')
    .map((_, el) => {
      const card = $(el);
      const link = card.find('h3 a');
      const ratingWord = card.find('p.star-rating').attr('class').replace('star-rating', '').trim();
      return {
        title: link.attr('title'),
        price: Number(card.find('.price_color').text().replace(/[^0-9.]/g, '')),
        rating: RATINGS[ratingWord] ?? null,
        inStock: card.find('.availability').text().includes('In stock'),
        url: new URL(link.attr('href'), pageUrl).href,
      };
    })
    .get();

  // Fail loudly when the layout changes instead of saving an empty file
  if (books.length === 0) throw new Error(`No books found on ${pageUrl}; check the selectors`);
  return books;
}

Testen Sie ihn dann mit dem in Node integrierten Test-Runner, ohne zusätzliche Abhängigkeiten:

// Run with: node --test
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { parseBooks } from './parse-books.mjs';

const PAGE_URL = 'https://books.toscrape.com/catalogue/page-1.html';

test('parses every book card on a saved page', async () => {
  const html = await readFile(new URL('./fixtures/books-page-1.html', import.meta.url), 'utf8');
  const books = parseBooks(html, PAGE_URL);

  assert.equal(books.length, 20);
  for (const book of books) {
    assert.ok(book.title.length > 0);
    assert.ok(book.price > 0);
    assert.ok(book.rating >= 1 && book.rating <= 5);
    assert.match(book.url, /^https:\/\/books\.toscrape\.com\/catalogue\//);
  }
});

test('throws when the layout changes', () => {
  assert.throws(() => parseBooks('<html><body><p>Redesigned</p></body></html>', PAGE_URL), /No books found/);
});

Führen Sie node --test, und die Zusammenfassung endet mit:

# tests 2
# pass 2
# fail 0

Der erste Test überprüft, ob der Parser alle 20 Karten mit sinnvollen Werten liest; der zweite, ob eine neu gestaltete Seite einen Fehler auslöst, anstatt ein leeres Array zurückzugeben. Da sich das Fixture auf der Festplatte befindet, laufen die Tests offline. Wenn sich die Website ändert, speichere ein neues Fixture und aktualisiere die Selektoren, bis die Tests bestehen.

Ein paar Gewohnheiten sorgen dafür, dass Selektoren länger halten:

  • Verwenden Sie vorzugsweise IDs, data-* Attribute und itemprop Attribute. Sie sind aussagekräftig und ändern sich seltener als Styling-Klassen.
  • Vermeiden Sie Utility-Klassen und generierte Klassennamen, die sich bei jedem Deploy ändern können.
  • Orientieren Sie sich eher an der Struktur als an der Position. „Der Preis in jeder Produktkarte“ überdauert ein neues Banner; „das dritte `div`“ hingegen nicht.

Wenn die Pflege der Abrufebene mehr kostet als das Parsen, vergleichen Sie Web-Scraping-APIs hinsichtlich Aufwand und Kosten.

Häufig gestellte Fragen

Ist Cheerio schneller als Puppeteer?

In der Regel bei statischen Seiten. Cheerio parst nur Text, während Puppeteer einen vollständigen Browser startet, Ressourcen lädt und Skripte ausführt, sodass Cheerio pro Seite weitaus weniger Arbeit leistet. Messen Sie anhand Ihres eigenen Ziels, anstatt sich auf allgemeine Benchmarks zu verlassen. Wenn die Daten JavaScript benötigen, um angezeigt zu werden, kann Cheerio allein sie bei keiner Geschwindigkeit abrufen.

Kann Cheerio JavaScript ausführen oder dynamische Seiten scrapen?

Nein. Cheerio analysiert die von Ihnen angegebene HTML-Zeichenkette und führt niemals Skripte aus. Suchen Sie bei dynamischen Seiten zunächst nach den Daten in Inline-Skripten oder in den JSON-Anfragen der Seite. Wenn beides nicht funktioniert, holen Sie sich das gerenderte HTML von einem Headless-Browser oder einer Rendering-API und übergeben Sie es an `cheerio.load()`.

Benötige ich Axios, um Cheerio zu verwenden?

Nein. Aktuelle Node.js-Versionen enthalten eine globale `fetch`-Funktion, sodass du `await fetch(url)` aufrufen und `await res.text()` an `cheerio.load()` übergeben kannst. Auch Cheerios `fromURL()` kann Daten für dich abrufen. Wenn dein Projekt bereits Axios verwendet, funktioniert das ebenfalls: Übergebe `response.data` auf die gleiche Weise an `cheerio.load()`.

Unterstützt Cheerio XPath?

Nein. Cheerio wählt Elemente mit CSS-Selektoren sowie einigen Erweiterungen im jQuery-Stil wie :contains() und :first aus und verfügt über keine XPath-Engine. Die meisten XPath-Ausdrücke lassen sich in einen CSS-Selektor plus Durchlaufmethoden wie `.parent()`, `.next()` oder `.filter()` übersetzen. Wenn du auf XPath angewiesen bist, verwende einen Parser, der dies unterstützt.

Funktioniert Cheerio mit TypeScript?

Ja. Cheerio liefert eigene Typdefinitionen mit, sodass kein separates @types-Paket dafür installiert werden muss. Importiere „load“ und den Typ „CheerioAPI“, versehe den Rückgabetyp deines Parsers mit einer Annotation und führe die Datei dann mit „tsx“ aus oder füge „@types/node“ hinzu und kompiliere mit „tsc“. Die Parsing-Logik bleibt identisch mit der JavaScript-Version.

Warum gibt `cheerio.load` ein leeres Ergebnis zurück?

In der Regel sind die Elemente nicht im rohen HTML enthalten. Möglicherweise hast du Selektoren aus den DevTools kopiert, die die Seite nach Ausführung der Skripte anzeigen, oder der Server hat eine Blockseite oder einen Fehler zurückgegeben. Protokolliere den Statuscode und die ersten Zeilen des HTML-Codes und vergleiche deinen Selektor anschließend mit dem Quelltext.

Wie erhalte ich mit Cheerio reinen Text aus HTML?

Rufe .text() für die Auswahl auf und normalisiere anschließend die Leerzeichen mit .replace(/\s+/g, ' ').trim(). Die Methode .text() fügt den Text aller verschachtelten Elemente zusammen und behält dabei Zeilenumbrüche und Einrückungen aus der Quelle bei. Bei einer ganzen Seite sollten Sie zunächst das Hauptinhaltselement auswählen, um Navigations- und Fußzeilentext aus Ihrem Ergebnis auszuschließen.

Kann Cheerio XML- und RSS-Feeds analysieren?

Ja. Übergeben Sie { xml: true } als Option: cheerio.load(xml, { xml: true }). Im Standard-HTML-Modus werden Tags wie „link“ als leere HTML-Elemente behandelt und verlieren ihren Text, was die RSS-Analyse beeinträchtigt. Der XML-Modus berücksichtigt zudem die Groß- und Kleinschreibung von Tag-Namen und überspringt die Wrapper „html“, „head“ und „body“.

Wie vermeide ich es, beim Scraping mit Cheerio blockiert zu werden?

Eine Sperrung erfolgt auf Seiten der Anfrage, nicht in Cheerio. Senden Sie einen ehrlichen User-Agent, fügen Sie eine Verzögerung zwischen den Anfragen ein, wiederholen Sie 429- und 5xx-Antworten mit Backoff, beachten Sie „Retry-After“ und halten Sie sich an „robots.txt“ sowie die Nutzungsbedingungen der Website. Wenn Seiten, die Sie erfassen dürfen, dennoch 403-Fehler zurückgeben, ändern Sie die Abrufebene und behalten Sie Ihren Parser bei.

Cheerio ist lediglich ein Parser, daher hängt die Rechtmäßigkeit davon ab, was und wie Sie Daten erfassen. Öffentlich zugängliche Daten, die höflich und im Rahmen der Nutzungsbedingungen einer Website erfasst werden, bergen im Allgemeinen ein geringeres Risiko. Personenbezogene Daten, Inhalte hinter Login-Seiten und urheberrechtlich geschütztes Material sind mit zusätzlichen Verpflichtungen verbunden. Die Vorschriften unterscheiden sich je nach Land; holen Sie daher bei kommerziellen Projekten rechtlichen Rat ein. Dies ist keine Rechtsberatung.

Wann solltest du das Abrufen einer API überlassen?

Nutzen Sie Cheerio für das, was es am besten kann: HTML in saubere Daten umzuwandeln. Wenn Seiten JavaScript benötigen oder immer wieder 403-Fehler zurückgeben, lassen Sie sie von etwas anderem abrufen. Die Tarife für die Scraper-API beginnen mit einer kostenlosen Testphase für 5.000 Anfragen. JavaScript-Rendering und Geotargeting kosten keine zusätzlichen Credits, und fehlgeschlagene Anfragen werden nicht in Rechnung gestellt. Für Abläufe, die Klicks, Scrollen oder Wartezeiten erfordern, fordern Sie ein auf Ihr Arbeitsvolumen zugeschnittenes Angebot für die Browser-API an. Ihr Parser bleibt in beiden Fällen derselbe.

Über den Autor

Raluca Penciuc, Full-Stack-Entwickler @ WebScrapingAPI

Raluca Penciuc

Full-Stack-Entwickler

Raluca Penciuc ist Full-Stack-Entwicklerin bei WebScrapingAPI. Sie entwickelt Scraper, verbessert Umgehungsstrategien und findet zuverlässige Wege, um die Erkennung auf Zielwebsites zu verringern.

Web-Scraping mit AWS Lambda: Leitfaden für Python und Java 2026
Anleitungen

Web-Scraping mit AWS Lambda: Leitfaden für Python und Java 2026

TL;DR: Kurz gesagt: Web-Scraping mit AWS Lambda funktioniert am besten, wenn jeder Aufruf kurz, begrenzt und unabhängig wiederholbar ist. Beginnen Sie mit direktem HTTP, AWS SAM und S3 und fügen Sie erst dann SQS, Container, Browser-Rendering, Proxys oder eine verwaltete Abrufebene hinzu, wenn sich im Rahmen der Arbeitslast herausstellt, dass diese erforderlich sind.

Suciu Dan26 min read
Artikel lesen
So verwenden Sie GoSpider: Crawlen, URLs bereinigen und Daten extrahieren
Anleitungen

So verwenden Sie GoSpider: Crawlen, URLs bereinigen und Daten extrahieren

TL;DR: GoSpider ist ein Befehlszeilen-Crawler zum Auffinden von URLs, kein vollständiger Scraper für strukturierte Daten. Diese Anleitung zur Verwendung von GoSpider zeigt einen begrenzten Crawl, die saubere Verarbeitung der Ausgabe, die Übergabe von Colly an CSV sowie einen Diagnosepfad für 403-Antworten oder Seiten, die eine JavaScript-Darstellung erfordern.

Suciu Dan20 min read
Artikel lesen
Wie man Redfin scrappt: Python-Leitfaden für Immobiliendaten
Anleitungen

Wie man Redfin scrappt: Python-Leitfaden für Immobiliendaten

TL;DR: Redfin stellt versteckte API-Endpunkte zur Verfügung, die strukturiertes JSON für Immobilienangebote zurückgeben, wodurch das fragile HTML-Parsing vollständig übersprungen werden kann. Diese Anleitung führt Sie durch den Aufbau eines Python-Scrapers, der Miet- und Verkaufsdaten extrahiert, nach Standort sucht, neue Angebote über XML-Sitemaps überwacht und saubere Ergebnisse in CSV oder JSON exportiert.

Suciu Dan11 min read
Artikel lesen

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.