Eine leere Seitenaufnahme sollte zu einem Extraktionsfehler führen. In unserem Test lieferte sie ein Mainstays 5-Piece Dining Set für $149, mit 4,5 Sternen und 120 Bewertungen. Unter der Walmart-URL wurde tatsächlich ein elektrischer Multikocher für $59 verkauft. Der vom Modell erfundene Datensatz bestand unser JSON Schema, während der integrierte Walmart-Scraper in allen drei Läufen den Multikocher zurückgab.
Dieses Beispiel bringt ein Problem der strukturierten Datenextraktion auf den Punkt: Wohlgeformtes JSON kann trotzdem das falsche Produkt beschreiben. Um den Unterschied zu messen, haben wir drei Pfade auf 25 Amazon- und 25 Walmart-Produktseiten getestet: einen benannten Crawlbase-Scraper, autoparse=true, sowie Markdown mit anschließendem striktem JSON-Schema-Aufruf an GPT-4o-mini. Jeder Pfad lief dreimal. Gemessen haben wir Feldabdeckung, Wiederholbarkeit, Werte, die im erfassten Text nicht vorkommen, Latenz sowie die Anzahl der Requests und Modell-Token.
Das Begleit-Repository enthält die Implementierung, neun Walkthrough-Skripte, Offline-Fixtures und Tests. Seine URL-Liste definiert die Kandidaten und Reserven, und die aufgezeichneten Ergebnisse bewahren die Messwerte der Läufe vom 2. November 2026.
- Die beiden Parser-Pfade bestanden das Anwendungsschema in 297 von 300 Versuchen, ohne ein einziges Modell-Token. Der Pfad Markdown plus GPT-4o-mini wurde in 130 von 150 Fällen akzeptiert.
- Strikte strukturierte Ausgabe beseitigte Parse-Fehler, nicht Erfindungen. Das Modell lieferte für jede Bot-Prüfung und jeden leeren Body, den es bekam, ein vollständiges Objekt, und eines dieser Objekte bestand das Schema.
- Probieren Sie zuerst den gepflegten Parser, normalisieren Sie, validieren Sie gegen Ihren eigenen Vertrag und rufen Sie das Modell nur für die Datensätze auf, die durchfallen. Der Router im Repository macht genau das.
Wählen Sie, wie aus der Seite JSON wird
Die Crawling API unterstützt sowohl den reinen Abruf als auch gepflegte Extraktion. In einem Amazon-Scraper-API-Workflow liefert das Senden von scraper=amazon-product-details Produkt-JSON. Walmart verwendet scraper=walmart-product-details. Mit autoparse=true, dem automatischen Modus, erkennt die API einen unterstützten Seitentyp und wendet den passenden Scraper an.
Die ersten Probeabfragen lieferten über autoparse und den benannten Scraper dasselbe Produktobjekt. Passt kein unterstützter Scraper, nutzt Crawlbase ersatzweise den generic-extractor, der allgemeine Seitendaten liefert. Es bleiben trotzdem getrennte Requests: Der spätere Benchmark enthält Unterschiede bei den zurückgegebenen Werten und bei Timeouts. Deterministische Extraktion beschreibt den Parsing-Ansatz; sie macht weder eine live ausgelieferte Händlerseite noch jede einzelne Antwort unveränderlich.
Die LLM-Route holt die Seite per format=md, schickt dann Markdown, Produkt-URL, Anweisungen und ein Schema mit acht Feldern an OpenAI. Bei einem erfolgreichen Versuch ohne Cache-Treffer kommt so ein Modellaufruf zum Crawl hinzu. Die Übersicht zur KI-Datenextraktion erklärt dieses Muster aus Abruf und anschließender Extraktion; hier vergleichen wir es mit den gepflegten Produkt-Parsern, wobei dieselbe Datenextraktions-API für die Beschaffung dient.
Eine Probeabfrage testete außerdem scraper= zusammen mit format=md auf Amazon. Die Antwort enthielt nur das Scraper-JSON. Der Benchmark holt geparste Daten und Markdown deshalb getrennt. Das ist für die Interpretation seiner Belegprüfungen wichtig: Das Modell wird gegen seinen eigenen erfassten Input geprüft, jeder Parser dagegen gegen einen weiteren Crawl derselben URL.
Definieren Sie einen Datensatz, bevor Sie Extraktoren vergleichen
Die drei Pfade brauchen eine gemeinsame Darstellung, bevor sich ihre Ausgaben vergleichen lassen. Das gemeinsame Schema mit den Mappings definiert acht Felder: title, id, brand, price, currency, availability, rating, sowie reviews_count.
Die Live-Antworten der Scraper verwendeten andere Schlüssel:
| Gemeinsames Feld | Amazon-Antwort | Walmart-Antwort |
|---|---|---|
title |
name |
title |
id |
ASIN innerhalb von productInformation, mit URL-Fallback im Mapper |
itemId |
brand |
brand |
brand |
price |
rawPrice, mit Fallback auf price
|
price |
currency |
currency |
currency |
availability |
inStock |
Kein Lagerbestandsfeld in den aufgezeichneten Antworten |
rating |
customerReview |
ratings |
reviews_count |
customerReviewCount |
reviewsCount |
Diese beobachteten Schlüssel wichen von den dokumentierten Namen ab, die während der Entwicklung verwendet wurden; die Mappings folgen deshalb den aufgezeichneten Payloads. Die Walmart-Verfügbarkeit bleibt null, wenn kein Lagerbestandsfeld geliefert wird. Der Mapper verarbeitet outOfStock oder availability ebenfalls, falls eines davon in einer Antwort auftaucht.
Die LLM-Schema-Extraktion verwendet einen eigenen Ausgabevertrag. Jeder Schlüssel muss vorhanden sein, aber jeder Wert darf JSON null sein. Der Prompt verlangt, nur Werte zu verwenden, die auf der Seite stehen, und jede Feldbeschreibung weist das Modell an, null zurückzugeben, wenn der Wert fehlt:
MAX_MARKDOWN_CHARS = 60_000 SYSTEM_PROMPT = ( "You extract one product record from a retail product page converted to Markdown. " "Use only values printed on the page. If a value is not on the page, return null. " "Do not guess, convert, or fill in values from general knowledge." )
Der LLM-Client fordert strikte strukturierte Ausgabe bei Temperatur 0 an und sendet die ersten 60.000 Markdown-Zeichen:
response = self.client.chat.completions.create( model=self.model, temperature=0, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": f"URL: {url}\n\n{page}"}, ], response_format={ "type": "json_schema", "json_schema": {"name": "product_v1", "strict": True, "schema": LLM_SCHEMA}, }, )
Nach der Extraktion normalisiert die Anwendung das Ergebnis und validiert es gegen product.v1. Dieses Akzeptanzschema verlangt einen Titel und eine ID mit mindestens drei Zeichen, nichtnegative Preise und Zähler, Bewertungen von 0 bis 5 sowie einen zulässigen Lagerbestandswert oder null. Ein numerischer Preis erfordert einen Währungsstring aus drei Großbuchstaben:
PRODUCT_SCHEMA = { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": SCHEMA_VERSION, "type": "object", "required": FIELDS + ["_meta"], "additionalProperties": False, "properties": { "title": {"type": "string", "minLength": 3}, "id": {"type": "string", "minLength": 3}, "brand": {"type": ["string", "null"]}, "price": {"type": ["number", "null"], "minimum": 0}, "currency": {"type": ["string", "null"], "pattern": "^[A-Z]{3}$"}, "availability": {"enum": AVAILABILITY + [None]}, "rating": {"type": ["number", "null"], "minimum": 0, "maximum": 5}, "reviews_count": {"type": ["integer", "null"], "minimum": 0}, "_meta": { "type": "object", "required": ["url", "path", "extractor", "schema_version"], "properties": { "url": {"type": "string"}, "path": {"enum": ["scraper", "autoparse", "markdown_llm"]}, "extractor": {"type": "string"}, "schema_version": {"const": SCHEMA_VERSION}, }, }, }, "allOf": [ {"if": {"properties": {"price": {"type": "number"}}, "required": ["price"]}, "then": {"properties": {"currency": {"type": "string"}}}}, ], }
Der Code normalisiert gängige Währungssymbole, aber dieses Muster allein prüft nicht die Zugehörigkeit zur Währungsliste nach ISO 4217. Der Unterschied zwischen diesen beiden Schemas erklärt die Ergebnisse: Das Modell kann ein vollständiges, strukturell gültiges Objekt mit null-Werten zurückgeben, das die Anwendung später ablehnt, weil der Titel fehlt.
Vergleichen Sie 50 Seiten über drei Läufe
Das Benchmark-Harness beginnt mit 25 Amazon-URLs unter /dp/ sowie 25 Walmart-URLs unter /ip/ und Reserven. Das Markdown des ersten Laufs jeder ausgewählten URL muss länger als 1.500 Zeichen sein und die Produkt-ID enthalten. So gilt dieselbe Eintrittsregel, bevor die drei Pfade gemessen werden, auch wenn der Datensatz damit nur Seiten enthält, die brauchbares Markdown geliefert haben.
Die Auswahl ersetzte 28 Kandidaten. Amazon steuerte sieben bei: sechs 404-Seiten und einen nicht verfügbaren Artikel, dessen Seite ihre ASIN nicht nannte. Walmart steuerte 21 bei: sechs Bot-Prüfungen, zehn Consent-Dialoge, zwei leere Bodys, zwei Nicht-gefunden-Seiten und eine Seite, die die Eintrittsregel nicht erfüllte. Fünf Walmart-Kandidaten und alle Walmart-Reserven stammten aus einem einzigen organischen Listing per walmart-serp für "kitchen essentials".
Die drei Läufe fanden am 2. November 2026 direkt hintereinander statt. Die veröffentlichte Matrix enthält 450 Pfadergebnisse: 50 URLs mal drei Pfade mal drei Läufe. Von diesen beibehaltenen Ergebnissen nutzten 412 den Normal token und 38 nach einem Retry den JavaScript token. Diese Summen beschreiben aufgezeichnete Ergebnisse; Requests für die URL-Auswahl und zusätzliche Retry-Versuche kommen bei der tatsächlichen Netzwerkarbeit hinzu.
Die Python-Requirements pinnen das Crawlbase SDK 1.1.0. Die aufgezeichneten LLM-Aufrufe forderten gpt-4o-mini an und erhielten gpt-4o-mini-2024-07-18.
Der Crawl-Client setzt ein Client-Timeout von 120 Sekunden. Ist ein JavaScript token konfiguriert, erlaubt er einen einzigen Wechsel nach cb_status 207 oder 503 oder nach einer erfolgreichen Antwort, die eine bekannte Challenge, einen fast leeren Markdown-Body oder ein Parser-Ergebnis ohne Titel enthält. Ursprüngliche 404- und 410-Antworten sind von diesem Retry ausgenommen. Transportausnahmen zeichnet dieser Client als Fehler auf, statt sie zu wiederholen:
TIMEOUT_S = 120 RETRY_ON_JS = {"207", "503"} # A 200 from the API can still carry an interstitial instead of the product page. CHALLENGE_MARKERS = ("robot or human", "health information consent", "enter the characters you see below") MIN_MARKDOWN_CHARS = 40 def unusable(result: "CrawlResult") -> bool: """Same rule for every path: no product content in an otherwise successful response.""" if result.cb_status != "200" or result.error: return False if result.path == "markdown": text = (result.markdown or "").lower() return len(text) < MIN_MARKDOWN_CHARS or any(m in text for m in CHALLENGE_MARKERS) body = result.data.get("body") if isinstance((result.data or {}).get("body"), dict) else result.data return not (isinstance(body, dict) and (body.get("name") or body.get("title")))
Das Metrik-Modul misst mehrere verschiedene Eigenschaften. Die Füllrate ist die mittlere Nicht-null-Abdeckung über die acht Felder. Versuche ohne Datensatz gehen mit keinem gefüllten Feld ein; vom Anwendungsschema abgelehnte Datensätze tragen ihre Nicht-null-Felder weiterhin bei. Konsistenz verlangt, dass der normalisierte Datensatz mit acht Feldern in allen drei Läufen übereinstimmt; Metadaten sind ausgenommen. Die Schema-Akzeptanz zählt Datensätze mit bestandenem product.v1, und die Latenz umfasst die Crawl-Dauer (bei einem JavaScript-Retry beide Versuche) plus, beim LLM-Pfad, die Modelldauer.
Bei der Seitentext-Prüfung werden die Werte des LLM mit dem Markdown-Präfix verglichen, das es erhalten hat, wobei die URL zusätzlich als Beleg für die Produkt-ID dient. Parser-Werte werden mit dem separaten Markdown-Crawl desselben Laufs verglichen; die Prüfung entfällt, wenn diese Aufnahme die Produktseiten-Regel nicht erfüllt. Der Benchmark deaktiviert bewusst das Markdown-Content-Gate des Routers, um zu messen, was das Modell mit Bot-Prüfungen und leeren Seiten macht.
Lesen Sie die gemessenen Ergebnisse
| Gemessen | Integrierter Scraper | autoparse |
Markdown + gpt-4o-mini |
|---|---|---|---|
| Mittlere Füllrate, Amazon | 88,3 % | 89,0 % | 90,5 % |
| Mittlere Füllrate, Walmart | 83,0 % | 86,5 % | 74,2 % |
| Identisch in allen 3 Läufen, Amazon | 12 von 25 | 13 von 25 | 12 von 25 |
| Identisch in allen 3 Läufen, Walmart | 19 von 25 | 17 von 25 | 2 von 25 |
| Werte nicht im Seitentext, Amazon | 36 von 522 | 26 von 526 | 10 von 543 |
| Werte nicht im Seitentext, Walmart | 6 von 353 | 10 von 374 | 21 von 445 |
| Datensätze, die das Schema bestanden | 147 von 150 | 150 von 150 | 130 von 150 |
| Mediane Latenz pro Seite | 13,9 s | 15,6 s | 13,4 s |
| Modell-Token | 0 | 0 | 1.803.338 |
Die beiden Parser-Pfade bestanden das Schema in 297 von 300 Versuchen ohne Modell-Token. Alle drei Fehlschläge des benannten Scrapers waren Walmart-Aufrufe, die in das Client-Timeout liefen. Das LLM lieferte bei jedem Versuch ein Objekt, aber Normalisierung und Anwendungsvalidierung akzeptierten 130 der 150 Datensätze.
Bei Walmart zeigte sich die größte Trennung. Von seinen 75 Markdown-Aufnahmen enthielten 21 eine Bot-Prüfung, einen Consent-Dialog oder einen leeren Body. Alle 21 traten unter den 50 Aufnahmen der Läufe zwei und drei auf, nachdem die Auswahl im ersten Lauf die URLs bereits zugelassen hatte. Das Modell erzeugte trotzdem für jede davon ein Objekt. Fünfzehn Titel waren der String "null", und 16 Antworten lieferten in_stock ohne stützenden Lagerbestandstext. Der erfundene Essgruppen-Datensatz aus der Einleitung war die eine Walmart-Aufnahme ohne Produkt, die nach der Normalisierung trotzdem das Anwendungsschema bestand.
Amazon verlangt eine andere Lesart. Das LLM hatte eine etwas höhere Feldabdeckung, und seine Seitentext-Prüfung markierte weniger Werte als jeder der beiden Parser. Bei 27 der 36 markierten Werte des benannten Scrapers gab das Modell beim Lesen des Markdowns desselben Laufs für dieses Feld null zurück. Die aufgezeichneten Aufnahmen enthielten listenartige Layouts ohne Buy Box, sodass manche Abweichungen auf unterschiedliche Inhalte zurückgingen, die separate Crawls einer URL zurücklieferten.
Preis, Bewertung und Anzahl der Rezensionen änderten sich zwischen den Läufen bei 11 oder 12 Amazon-URLs je Parser-Pfad und bei 7 bis 11 URLs beim Modell-Pfad. Modelltitel änderten sich bei fünf Amazon-URLs; Parser-Titel bei keiner. Diese Beobachtungen zeigen, warum Konsistenz zusammen mit den erfassten Eingaben interpretiert werden muss. Bei keiner URL war das vollständige Markdown über alle drei Läufe byte-identisch, das Experiment hat Modellzufall also nicht von Seitenvariation isoliert. Unter den neun Walmart-URLs mit Produktseiten-Markdown in jedem Lauf war der LLM-Datensatz bei zwei identisch, der des benannten Scrapers bei sieben.
Die niedrigere LLM-Latenz bei Walmart schließt auch kurze Zwischenseiten und leere Eingaben ein. Eine schneller gescheiterte Eingabe ist kein Beleg für schnellere erfolgreiche Produktextraktion. Ebenso gilt der angegebene Median pro Versuch, nicht als Zeit pro akzeptiertem Datensatz oder als Gesamtdurchsatz eines Batches.
Prüfen Sie, was die zurückgegebenen Werte tatsächlich bedeuten
Der Seitentext-Checker sucht nach normalisierten Belegen für jeden Wert. Er erwischt sowohl Erfindungen des Modells als auch Abweichungen der Parser. Auf einer Walmart-Seite gab der benannte Scraper reviewsCount als "122" zurück, während die Seite 12.292 zeigte; autoparse lieferte "12292". Auf einer anderen Seite tauschten die beiden Pfade die Rollen. Die Feldabdeckung allein zählte den falschen Zähler als gefüllt.
def is_supported(field: str, value, text: str) -> bool: if value is None: return True if field in ("title", "brand", "id"): return _has_text(text, str(value)) if field == "price": return _has_number(text, float(value)) if field == "reviews_count": return _has_number(text, float(value)) or _has_abbreviated(text, float(value)) if field == "rating": return _has_rating(text, float(value)) if field == "currency": return any(cue in text for cue in CURRENCY_CUES.get(value, (value.lower(),))) if field == "availability": cues = IN_STOCK_CUES if value == "in_stock" else OUT_OF_STOCK_CUES return any(cue in text for cue in cues) return True def invented_fields(record: dict, markdown: str, fields: list[str], url: str = "") -> list[str]: """The URL counts as input: an id taken from it is not invented.""" text = page_text(markdown) + " " + url.lower() return [f for f in fields if record.get(f) is not None and not is_supported(f, record[f], text)]
Eine Handprüfung von drei Amazon- und zwei Walmart-Seiten führte zu vier Verfeinerungen. Abgekürzte Zähler werden mit ihrer angezeigten Genauigkeit akzeptiert: 27.406 passt zu "27.4K" und 14.084 zu "14K". Der Textabgleich ignoriert Leerzeichen, sodass "HeroCosmetics" zu "Hero Cosmetics" passt. Walmarts "Pickup as soon as 3pm" zählt als Hinweis auf verfügbaren Bestand. Walmart-Bewertungen sind bei allen Pfaden von der Belegprüfung ausgenommen, weil das erfasste Markdown die Bewertungen anderer Produkte statt der des Artikels selbst zeigte.
Diese Ausnahme entfernt Bewertungen weder aus der Füllraten-Berechnung noch aus den Nicht-null-Summen, die als Nenner der Belegrate dienen. Die berichteten Zähler und Diagrammprozente sind deshalb als Textbeleg-Messungen dieses Harness zu lesen, nicht als Genauigkeitswert über einen identisch geprüften Satz von Feldern.
Zwei von Hand geprüfte Abweichungen zeigen die verbleibende Grenze. Der Checker akzeptierte out_of_stock in einem Fall, in dem nur ein Open-Box-Angebot nicht verfügbar war, und akzeptierte eine Rezensionszahl von 30, weil an anderer Stelle der Seite eine unzusammenhängende "30" vorkam. Textpräsenz belegt, dass ein Wert irgendwo in der Eingabe vorkommt; ihn dem richtigen Produkt, Angebot und Feld zuzuordnen, erfordert Kontext.
Strikte Ausgabe löste ein anderes Problem. Die 150 Modellaufrufe erzeugten keine JSON-Parse-Fehler und keine unvollständigen Objekte, aber die strikte Struktur verhinderte weder unbelegte Lagerbestandswerte noch ein erfundenes Produkt. Wer jeden Schlüssel verlangt, erlaubt weiterhin ein ehrliches JSON null. Der Fehler entstand, als das Modell ein Feld ohne Beleg füllte, nicht weil das Schema eine Nicht-null-Antwort verlangte.
Für verwandte schemagetriebene Workflows bietet Firecrawl Extract eine Extraktionsschnittstelle, deren Dokumentation inzwischen /agent als Nachfolger einführt. Die Hinweise zum JSON-Modus empfehlen explizite null-Behandlung, fokussierte Prompts, kleinere Schemas ab etwa 30 Feldern und das Entfernen der Array-Constraints minItems und maxItems aus dem Schema, da sie nicht steuern, wie viel Inhalt gesammelt wird. Der gemessene LLM-Pfad in diesem Artikel ist ein direkter OpenAI-Aufruf über Crawlbase-Markdown.
Die Issue-Reports zeigen außerdem, warum Ausgabefehler getrennt diagnostiziert werden müssen. Die Firecrawl-Reports #1836 und #3300 betreffen die Umwandlung von Zod nach JSON Schema, die leere oder fehlende Daten erzeugte. Report #2491 betrifft eine Integration mit einem selbst gehosteten Modell, die gar kein Objekt erzeugte. Diese Beispiele beschreiben Schema- und Integrationsfehler; sie messen keine Halluzinationshäufigkeit und belegen nicht, dass der strikte Modus sie beheben würde.
Normalisieren und validieren Sie, bevor Sie zurückfallen
Sobald die Fehlermodi sichtbar sind, wird die Routing-Regel einfach: Probieren Sie den unterstützten Parser, normalisieren Sie seine Ausgabe und akzeptieren Sie sie nur, wenn sie den Vertrag der Anwendung erfüllt. Der Normalisierer wandelt Platzhalter-Strings wie "null", "none", sowie "n/a" zu null um, parst Preise und abgekürzte Zähler, bereinigt Markenbezeichnungen und bildet Lagerbestandstext ab auf in_stock, out_of_stock, andernfalls null.
Titel mit dem String "null" in echte null-Werte umzuwandeln, machte aus 15 Schein-Titeln Ablehnungen durch das Anwendungsschema. Der Normalisierer löscht außerdem die Währung, wenn der Preis fehlt. Seine unterstützten Zahlenformate decken die Beispiele dieses Experiments ab, darunter $1,299.99 und €39,90; weitere Locales brauchen explizite Parsing-Regeln.
Der paketierte Router gibt das erste akzeptierte Ergebnis zurück:
def route(url: str, crawl: CrawlClient, llm: LLMExtractor, run: int = 1) -> Extraction: """First accepted record wins. A rejected record falls through to the next path.""" attempts = [] if scraper_for(url): attempts.append(lambda: via_scraper(url, crawl, run)) attempts.append(lambda: via_autoparse(url, crawl, run)) attempts.append(lambda: via_markdown_llm(url, crawl, llm, run)) result = None for attempt in attempts: result = attempt() if result.accepted: return result return result
Jede Funktion der Form via_* gibt ein Objekt vom Typ Extraction zurück. Auf eine erfolgreiche Extraktion folgen Normalisierung, Metadaten-Stempelung und Validierung gegen das JSON Schema. Dabei steht accepted dafür, dass ein Datensatz existiert und keine Validierungsfehler hat. Schlagen alle Pfade fehl, gibt der Router das letzte abgelehnte Ergebnis zurück, und write_record() schreibt nichts.
Vor einem Modellaufruf lehnt via_markdown_llm Markdown ab, das kürzer als 40 Zeichen ist oder eine von drei bekannten Challenge-Phrasen enthält. In diesen Fällen gibt sie not_a_product_page zurück:
def via_markdown_llm(url: str, crawl: CrawlClient, llm: LLMExtractor, run: int = 1, gate: bool = True) -> Extraction: res = crawl.fetch(url, "markdown", run) extractor = f"openai:{llm.model}" if not res.ok: return Extraction(url, "markdown_llm", extractor, None, [res.error or f"cb_status {res.cb_status}"], res.to_dict()) if gate and unusable(res): # A 200 that carries a challenge or an empty body never reaches the model. return Extraction(url, "markdown_llm", extractor, None, ["not_a_product_page"], res.to_dict()) out = llm.extract(url, res.markdown, run) if not out.ok: return Extraction(url, "markdown_llm", extractor, None, [out.error], res.to_dict(), out.to_dict()) return _finish(url, "markdown_llm", f"openai:{out.model or llm.model}", out.data, res.to_dict(), out.to_dict())
Angewendet auf die aufgezeichneten Walmart-Aufnahmen hätte dieses Gate alle 21 unbrauchbaren Seiten vom Modell ferngehalten. Es ist eine gezielte Inhaltsprüfung, kein vollständiger Detektor für jede Zwischenseite oder jede falsche Seite.
Die Akzeptanzentscheidung des Routers führt den Seitentext-Checker des Benchmarks nicht aus. Ergänzen Sie Ihre eigene Akzeptanzrichtlinie um kontextbezogene Belegprüfungen, wenn ein schemagültiger, aber falscher Preis- oder Lagerbestandswert nachgelagerte Entscheidungen beeinflussen würde. So bleiben strukturelle Validierung und Faktenprüfung explizite Implementierungsschritte.
Akzeptierte Datensätze enthalten _meta.url, _meta.path, _meta.extractor, sowie _meta.schema_version. Beispiele sind crawlbase:amazon-product-details, openai:gpt-4o-mini-2024-07-18, sowie product.v1. Sie identifizieren die Route und den Modell- oder Scraper-Namen; der Scraper-Name allein identifiziert keine bestimmte Parser-Revision.
Die Walkthrough-Skripte legen die einzelnen Schritte offen: den Aufruf des integrierten Scrapers, autoparse-Aufruf, LLM-Extraktion, sowie den Vergleich über drei Läufe.
Ein Endpunkt für die gesamte Route: ein gepflegter Produkt-Scraper, autoparse, alternativ sauberes Markdown für Ihr eigenes Modell, mit dem JavaScript token, wenn eine Seite einen Browser braucht. Fehlgeschlagene Requests werden nicht berechnet. Starten Sie mit bis zu 5.000 kostenlosen Requests, ohne Karte.
Token-Verbrauch und Caching
Der Disk-Cache ermöglicht wiederholte Analysen, ohne dieselbe Beschaffung zu wiederholen. Crawl-Einträge werden über Pfad, Laufnummer und URL adressiert. Modell-Einträge enthalten das angeforderte Modell, die Laufnummer, die URL und den Hash des vollständigen Markdowns. Keiner der beiden Caches läuft automatisch ab, und der Modellschlüssel enthält weder Prompt noch Schema. Löschen Sie die betreffenden Einträge, bevor Sie einen überarbeiteten Extraktionsvertrag messen oder frische Seiten holen.
Die aufgezeichneten Ergebnisse wurden aus gespeicherten Antworten neu berechnet. Null API-Aufrufe in den Metadaten dieses Durchlaufs beschreiben das Replay, nicht das ursprüngliche Experiment. Für die Neuberechnung dieser Messwerte ist der gespeicherte Antwort-Cache nötig; die veröffentlichte Metrikdatei allein enthält die Rohaufnahmen nicht.
Die Eingabegröße bleibt ein eigener Skalierungsfaktor bei den Kosten. Die 150 Modellaufrufe verbrauchten 1.793.923 Prompt-Token und 9.415 Completion-Token, zusammen 1.803.338. Amazon lag im Mittel bei 22.131 Token pro Aufruf; Walmart bei 1.914. Neunundvierzig der 75 Amazon-Aufrufe überschritten die Markdown-Grenze von 60.000 Zeichen und wurden abgeschnitten. Eine höhere Grenze schickt mehr Kontext; eine niedrigere riskiert, Felder am Seitenende zu verlieren.
Das Diagramm ist illustrativ. Bei fester mittlerer Eingabegröße wachsen beide Ansätze linear mit der Seitenzahl; der Modellpfad fügt pro Seite die Token-Verarbeitung hinzu. Seine steilere Linie gibt kein gemessenes Preis- oder Geschwindigkeitsverhältnis an. Der Benchmark zählt logische Crawl-Versuche und Modell-Token, während die tatsächlichen Beschaffungskosten auch von Retries, Domain-Komplexität, Rendering und Storage abhängen.
Das Release v2.11.0 von Firecrawl beschreibt einen weiteren Weg, wiederholte Modellarbeit zu reduzieren: deterministicJson erzeugt einen wiederverwendbaren Extraktor und cacht ihn pro Site. Dieser Mechanismus verfolgt dasselbe technische Ziel, Extraktionsarbeit über wiederholte Seiten zu amortisieren, war in diesem Experiment aber kein gemessener Pfad.
Sammeln in der Warteschlange und Ergebnisse per RID abrufen
Bei größeren Batches kann der Beschaffungsschritt unabhängig von dem Prozess abgeschlossen werden, der die URLs einreicht. Der Async-Walkthrough sendet async=true&store=true, erhält sofort eine Request-ID und holt das Ergebnis später aus dem Cloud Storage:
def submit(api, url: str) -> str | None: from urllib.parse import urlparse options = {"async": "true", "store": "true"} name = SCRAPERS.get(urlparse(url).hostname or "") options.update({"scraper": name} if name else {"autoparse": "true"}) res = api.get(url, options) payload = res.get("json") if not isinstance(payload, dict): try: payload = json.loads(res.get("body") or b"{}") except ValueError: payload = {} return payload.get("rid") # the SDK puts the async rid in res["json"], not res["rid"] def fetch_stored(storage, rid: str, attempts: int = 12, wait_s: float = 10.0) -> dict | None: for _ in range(attempts): try: res = storage.get(rid, {"format": "json"}) except KeyError: # crawlbase 1.1.0 raises this while the rid is not stored yet res = {} if res.get("status_code") == 200: stored = res.get("json_body") if isinstance(stored, dict) and stored: return stored body = res.get("body") or b"" if body: return {"body": body.decode("utf-8", "replace") if isinstance(body, bytes) else body} time.sleep(wait_s) return None
Mit SDK 1.1.0 liegt die asynchrone ID unter res["json"]["rid"]. Der Walkthrough pollt per RID und behandelt den beobachteten KeyError des SDK für einen noch nicht verfügbaren Eintrag als ausstehendes Ergebnis. Im Test mit zwei URLs war das Amazon-Ergebnis innerhalb von zwei Minuten lesbar. Walmart fehlte bei den ersten beiden Prüfungen und war bei einer späteren vorhanden. Das sind aufgezeichnete Beobachtungen zur Fertigstellung, kein garantiertes Lieferintervall.
Die Batch-Helfer akzeptieren auch eine Callback-URL. Crawlbase sendet das fertige Ergebnis per POST an diesen Endpunkt, während store=true eine Kopie zum Abruf behält. Der Walkthrough zu Zapier-Callbacks zeigt dieses Liefermuster. Das Async-Beispiel mappt und normalisiert abgerufene Payloads; wenden Sie dieselbe Anwendungsvalidierung an, bevor Sie sie als akzeptierte Produktdatensätze speichern.
Das Speichern einer Seite schlägt mit 0,5 Credits auf den Crawl auf, der sie geholt hat, laut Preisseite, und die Seite bleibt 30 Tage erhalten. Der Abruf ist kostenlos. Planen Sie Storage neben Crawl- und Modellarbeit in Ihr Budget ein.
Die Abrechnung hängt auch vom Antwortstatus ab. Die Referenz der Crawling API führt als abrechenbare Requests cb_status 200 oder 207 mit original_status 200, 201, 301, 302, 303, 307, 308, 404 oder 410 auf. Ein erkannter 503-Block wird nicht berechnet. Die Bot-Prüfungen in diesem Experiment kamen als erfolgreiche 200er an und wurden auch so berechnet, weshalb die Inspektion des Bodys auch nach der Statusprüfung nötig blieb.
Die API-Dokumentation empfiehlt asynchrone Lieferung für Batches über einigen hundert URLs und den Crawler für Jobs mit hohem Volumen wie Millionen von URLs. Für eigene Anforderungen an Durchsatz und Service ist das Enterprise-Angebot der nächste operative Schritt.
Setzen Sie das Modell dort ein, wo Interpretation Mehrwert bringt
Ein gepflegter Parser ist ein praktischer Ausgangspunkt für unterstützte Produktseiten. Er erspart den Modellaufruf pro Seite und liefert einen bekannten Satz von Feldern. Das Experiment zeigt auch den Kompromiss: Der Walmart-Lagerbestand war über den getesteten Parser nicht verfügbar, und falsche Rezensionszahlen mussten weiterhin geprüft werden. Wählen Sie die Extraktionsroute nach den Feldern, die Ihre Anwendung tatsächlich braucht.
Ein Modell ist nützlich für Shops ohne passenden Parser, für unregelmäßige Dokumente wie Forenthreads und Pressemitteilungen oder für einmalige Recherchen, bei denen ein eigener Extraktor mehr kosten würde als die Modellaufrufe. Solche Dokumente brauchen ihr eigenes Schema und eigene Mappings; dieser Router implementiert einen produktspezifischen Vertrag. Die Tutorials zu ChatGPT, Gemini, sowie Perplexity liefern verwandte Implementierungen nach dem Muster Abrufen und dann Interpretieren, während das Tutorial zur Amazon-Preisextraktion das Muster auf Handelsdaten anwendet.
Modelle können auch nach der deterministischen Extraktion arbeiten. Ein Produktdatensatz kann die Klassifikation in eine eigene Taxonomie oder das Matching von Kandidaten zwischen Walmart und Amazon stützen. Das Taggen von Beschwerden braucht Rezensionstext, liefern Sie diesen Inhalt also ebenfalls über einen passenden Scraper oder eine passende Aufnahme. Jede Aufgabe kann die Felder erhalten, die sie braucht, statt der gesamten Seite.
Geht es um wiederkehrende Preisbeobachtung, sammeln und normalisieren Sie zuerst den Preis und ergänzen dann bei Bedarf die Interpretation durch das Modell. Der Leitfaden zum geplanten Amazon-Scraping behandelt die wiederkehrende Beschaffung. Wer Beschaffung, Feldextraktion, Validierung und Interpretation explizit hält, kann Web Scraping nach JSON leichter prüfen, wenn sich ein Wert ändert.
Fazit
Auf diesen 50 Produktseiten erzeugten die Pfade mit gepflegtem Scraper 297 vom Schema akzeptierte Datensätze aus 300 Versuchen, ohne Modell-Token. Markdown plus GPT-4o-mini bot bei Amazon eine höhere Feldabdeckung, aber die Walmart-Zwischenseiten zeigten das Risiko, plausible Datensätze aus unbrauchbaren Eingaben zu akzeptieren. Die aufgezeichneten Ergebnisse sprechen dafür, mit einem verfügbaren Parser zu beginnen, die zurückgegebenen Felder zu prüfen und die Modellextraktion für die Lücken zu reservieren, die Ihre Anwendung füllen muss.
Die vollständige Implementierung liefert Mappings, Router, Benchmark und Offline-Beispiele. Um sie an Ihrem eigenen Katalog auszuprobieren, erstellen Sie ein Crawlbase-Konto, konfigurieren die Token und prüfen eine kleine Menge akzeptierter Datensätze, bevor Sie den Lauf ausweiten. Der Amazon-Scraper, Walmart-Scraper, sowie die umfassendere Übersicht zum E-Commerce-Scraping helfen dabei, die Beschaffungsroute für Ihre Ziele zu bestimmen.
Häufig gestellte Fragen (FAQs)
Für welche Websites gibt es integrierte Crawlbase-Scraper?
Der Scraper-Index führt unterstützte Extraktoren für Amazon, Walmart, eBay, AliExpress, Best Buy, Google, Bing, Facebook, Instagram, TikTok, LinkedIn, Reddit, Booking.com, Airbnb und weitere Sites sowie generische Extraktoren auf. Wählen Sie den Scraper für den Seitentyp und übergeben Sie seinen Namen über scraper=. Der Beispiel-Domain-Router bildet nur Amazon- und Walmart-Produktseiten ab; erweitern Sie ihn für weitere Ziele.
Funktioniert autoparse auf jeder Seite?
Mit autoparse=true, dem automatischen Modus, erkennt Crawlbase den Seitentyp und wendet einen passenden unterstützten Scraper an. Passt kein unterstützter Scraper, nutzt es ersatzweise den generic-extractor, der allgemeine Seitendaten statt eines händlerspezifischen Produktdatensatzes liefert. In diesem Repository bildet die Anwendung das zurückgegebene JSON auf ihr Produktschema ab und validiert es. Entsteht dabei kein akzeptabler Produktdatensatz, stellt die Anwendung einen separaten Markdown-Request und versucht die LLM-Extraktion. Dieser zusätzliche Fallback gehört zur Routing-Logik der Anwendung.
Wie oft gehen integrierte Scraper kaputt, und wer pflegt sie?
Crawlbase pflegt seine integrierten Scraper und aktualisiert sie, wenn Website-Änderungen die Extraktion beeinträchtigen. Ausfälle werden automatisch gemeldet und in der Regel innerhalb von Stunden behoben. Wenn Ihnen fehlende Felder, falsche Werte oder ein anhaltendes Problem auffallen, wenden Sie sich an den Crawlbase Support mit der betroffenen URL und einer Beispielantwort, damit das Team der Sache nachgehen kann.
Garantiert die Validierung per JSON Schema korrekt extrahierte Daten?
Nein. JSON Schema prüft Struktur, Pflichtfelder und Wertebeschränkungen des Datensatzes. Es prüft nicht, ob die Werte das richtige Produkt beschreiben. Prüfen Sie, ob die erfasste Seite brauchbaren Produktinhalt enthält, und verifizieren Sie wichtige Werte dann gegen Belege, die zum richtigen Produkt und Angebot gehören.
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 Daten-Pipelines liefert, statt Crawl-Infrastruktur zu warten. Bis zu 5.000 Requests kostenlos, keine Karte erforderlich.

