Eine KI-Datenpipeline ist nur so gut wie der Text, mit dem sie gespeist wird, und das offene Web ist die reichhaltigste Quelle frischer, domänenspezifischer Kenntnisse, auf die man ein Modell stützen kann. Das Problem liegt darin, diesen Text in eine nutzbare Form zu bringen: Die meisten Seiten sind ein Gewirr aus Navigation, Werbung und JavaScript-gerenderten Inhalten, die ein einfacher HTTP-Fetch nie zu sehen bekommt. Diese Anleitung zeigt, wie man eine KI-Datenpipeline mit LangChain und Crawlbase aufbaut, wobei die Crawling API als Dokumentenquelle genutzt wird, damit Seiten als sauberes Markdown ankommen, die dann mit Retrieval-Augmented Generation (RAG) aufgeteilt, eingebettet und abgefragt werden.
Die Pipeline hat eine einfache Form und läuft komplett in Python: Crawlbase ruft die Seite ab und bereinigt sie, LangChain teilt sie in Chunks auf und bettet sie in einen Vektorspeicher ein, und zur Abfragezeit werden die relevantesten Chunks abgerufen und einem LLM als Kontext übergeben. Crawlbase übernimmt Proxy-Rotation, Blocking-Behandlung und Rendering, sodass Ihr Pipeline-Code auf die Daten fokussiert bleibt, nicht auf den Kampf gegen Anti-Bot-Abwehr. Alles Folgende ist ausführbar; tauschen Sie Ihre eigenen URLs und Token ein und Sie haben ein funktionierendes RAG-System über Live-Webinhalte.
Warum Crawlbase als LangChain-Dokumentenquelle
LangChain liefert Dokument-Loader für Dateien, Datenbanken und eine Handvoll Web-Quellen, aber das Laden echter Webseiten im großen Maßstab ist der Punkt, an dem die meisten Pipelines ins Stocken geraten. Eine bloße Anfrage an eine moderne Site gibt entweder eine JavaScript-Hülle ohne Inhalt oder eine Sperrseite zurück, und selbst wenn man HTML zurückbekommt, ist es voller Boilerplate, die die Einbettungen verunreinigt. Schlechte Chunks bedeuten schlechtes Retrieval, was bedeutet, dass ein LLM selbstbewusst das Cookie-Banner zitiert.
Die Crawling API löst die Beschaffungsschicht sauber. Sie senden ihr eine URL, sie rendert die Seite hinter einer vertrauenswürdigen Residential-IP und kann den Inhalt als sauberes Markdown statt als rohes HTML zurückgeben. Dieses Markdown ist genau das, was Sie als LangChain-Dokument wollen: lesbarer Fließtext mit erhaltenen Überschriften und herausgefilterten Navigations-, Skript- und Werbeelementen. Das Einspeisen von vorbereinigtem Markdown in Ihren Splitter ist der größte Qualitätshebel in einer webbasierten RAG-Pipeline, und es ist derselbe Gedanke, der in LLM-bereitem Markdown-Web-Scraping erläutert wird.
Diese Trennung von Verantwortlichkeiten ist es, die die Pipeline wartbar hält. Crawlbase besitzt den Webzugang: IP-Rotation, CAPTCHA-Lösung, JavaScript-Rendering und strukturierte Ausgabe. LangChain besitzt die Orchestrierung: Chunking, Einbettung, Retrieval und den Prompt, der die Antwort einrahmt. Das Modell besitzt das Schlussfolgern. Sie können die Art des Chunkings oder das abgefragte Modell ändern, ohne die Datenbeschaffung anzufassen, und umgekehrt.
Die Crawling API akzeptiert einen format=markdown-Parameter (und einen get_markdown-Helfer im offiziellen Client), der die Seite als sauberes Markdown statt als HTML zurückgibt. Für RAG ist das wichtig: Markdown bewahrt Überschriften und Listen als Struktur, die Ihr Splitter respektieren kann, während Boilerplate wegfällt, die sonst zu lautem, minderwertigem Text in Ihrem Vektorspeicher würde.
Architektur: Von der URL zur fundierten Antwort
Die Pipeline hat vier Stufen, jede mit einer Aufgabe. Beschaffen: Die Crawling API ruft jede URL ab und gibt sauberes Markdown zurück. Aufteilen: LangChains Text-Splitter zerlegt jedes Dokument in überlappende Chunks, die klein genug sind, um präzise eingebettet und abgerufen zu werden. Einbetten und speichern: Jeder Chunk wird in einen Vektor umgewandelt und in einen Vektorspeicher geschrieben (lokal verwenden wir Chroma). Abrufen und generieren: Zur Abfragezeit betten Sie die Frage ein, holen die nächsten Chunks und übergeben sie einem LLM als Kontext.
Die ersten drei Stufen sind ein Offline-Ingestion-Job, den Sie ausführen, wenn sich Ihre Quellen ändern. Die vierte läuft jedes Mal, wenn ein Nutzer eine Frage stellt. Das Trennen von Ingestion und Abfrage ist es, was die Pipeline skalierbar macht: Sie crawlen und betten einmal ein, dann beantworten Sie viele Fragen günstig anhand der gespeicherten Vektoren. Das breitere Muster, einschließlich der Frage, warum Bereinigung wichtig ist, bevor Sie überhaupt einbetten, wird in Web-Scraping-Daten für KI und ML strukturieren und bereinigen behandelt.
Das Projekt einrichten
Sie benötigen Python 3.10 oder neuer. Erstellen Sie eine virtuelle Umgebung und installieren Sie die Bibliotheken: den offiziellen Crawlbase-Client für die Beschaffung, die LangChain-Pakete für die Orchestrierung, Chroma für den Vektorspeicher und die OpenAI-Integration für Einbettungen und das Chat-Modell.
python -m venv .venv source .venv/bin/activate pip install crawlbase langchain langchain-community langchain-openai langchain-chroma
Sie benötigen außerdem zwei Zugangsdaten: ein Crawlbase-Token aus Ihrem Dashboard und einen Embedding-/LLM-Anbieter-Schlüssel (hier ein OpenAI-Schlüssel). Das Paket crawlbase gibt Ihnen den CrawlingAPI-Client; langchain-chroma umhüllt den lokalen Chroma-Speicher; langchain-openai liefert sowohl die Einbettungen als auch das Chat-Modell. Exportieren Sie Ihre Schlüssel als Umgebungsvariablen, damit nichts Sensibles im Code landet.
export CRAWLBASE_TOKEN="your_crawlbase_token" export OPENAI_API_KEY="your_openai_key"
Schritt 1: Sauberes Markdown mit der Crawling API abrufen
Beginnen Sie mit der Beschaffung. Der offizielle Client stellt eine get-Methode bereit, die eine URL und Optionen entgegennimmt; die Übergabe von format=markdown gibt die Seite als sauberes Markdown im Antwort-Body zurück. Kapseln Sie das in eine kleine Funktion, die jede abgerufene Seite in ein LangChain-Document umwandelt und die Quell-URL in den Metadaten trägt, damit Sie sie später zitieren können.
import os from crawlbase import CrawlingAPI from langchain_core.documents import Document api = CrawlingAPI({"token": os.environ["CRAWLBASE_TOKEN"]}) def load_page(url): # format=markdown returns clean markdown, not raw HTML response = api.get(url, {"format": "markdown"}) if response["status_code"] != 200: raise RuntimeError(f"Fetch failed for {url}: {response['status_code']}") body = response["body"] text = body.decode("utf-8") if isinstance(body, bytes) else body return Document(page_content=text, metadata={"source": url}) urls = [ "https://example.com/docs/getting-started", "https://example.com/docs/pricing", ] docs = [load_page(u) for u in urls] print(f"Loaded {len(docs)} documents")
Fügen Sie für JavaScript-lastige Seiten "ajax_wait": "true" und einen "page_wait"-Wert in Millisekunden zum Options-Dict hinzu und verwenden Sie ein JavaScript-Token. Da die Beschaffung in load_page isoliert ist, hat das Aktivieren des Renderings keinen Einfluss auf nachgelagerte Stufen. Wenn eine Site mit einem Nicht-200-Status antwortet, wirft die Funktion mit dem Code, damit eine schlechte Quelle laut auftaucht, statt Ihren Speicher mit einer Fehlerseite zu vergiften.
Ihre RAG-Pipeline ist nur so gut wie der eingehende Text. Die Crawling API rendert die Seite hinter einer rotierenden Residential-IP und gibt in einem einzigen Aufruf sauberes Markdown zurück, sodass Ihre Chunks echter Inhalt sind statt Navigationsleisten und Sperrseiten. Binden Sie sie als Ihre LangChain-Dokumentenquelle ein und testen Sie sie zunächst mit ein paar öffentlichen URLs im kostenlosen Tarif.
Schritt 2: Dokumente in Chunks aufteilen
Ganze Seiten sind zu groß, um nützlich eingebettet zu werden: Ein einzelner Vektor für ein langes Dokument verwischt unterschiedliche Themen und schadet der Retrieval-Präzision. Teilen Sie jedes Dokument stattdessen in überlappende Chunks auf. LangChains RecursiveCharacterTextSplitter versucht zuerst an Absatz- und Satzgrenzen zu trennen, damit Chunks kohärent bleiben, und da das Markdown von Crawlbase Überschriften und Listen bewahrt, landen diese Splits an natürlichen Nähten.
from langchain_text_splitters import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=150, ) chunks = splitter.split_documents(docs) print(f"Split into {len(chunks)} chunks")
Eine chunk_size von etwa 1000 Zeichen mit einem Überlapp von 150 Zeichen ist ein vernünftiger Standard für Fließtext. Der Überlapp trägt etwas Kontext über Grenzen hinweg, damit eine Tatsache, die über zwei Chunks aufgeteilt wurde, nicht verloren geht. Passen Sie beides an Ihren Inhalt an: Dichtere, technischere Seiten liefern oft bessere Ergebnisse mit kleineren Chunks, während Langform-Artikel größere tolerieren. Die Metadaten aus load_page werden automatisch auf jeden Chunk kopiert, sodass jeder noch seine Quell-URL kennt.
Schritt 3: In einer Vektordatenbank einbetten und speichern
Wandeln Sie nun jeden Chunk in einen Vektor um und speichern Sie ihn. Ein Einbettungsmodell bildet Text auf einen Punkt in einem hochdimensionalen Raum ab, wo semantisch ähnliche Passagen nah beieinander liegen, was das Abrufen nach Bedeutung ermöglicht. Chroma speichert diese Vektoren lokal und übernimmt die Ähnlichkeitssuche; die Übergabe von persist_directory schreibt den Index auf die Festplatte, sodass Sie die Einbettungskosten nur einmal tragen.
from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vector_store = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", ) print(f"Stored {len(chunks)} vectors")
Dieser Block ist das Ende der Ingestion. Führen Sie ihn einmal aus, wenn sich Ihre Quellen ändern, nicht bei jeder Abfrage. Um den Speicher später wiederzuverwenden, öffnen Sie ihn mit Chroma(persist_directory="./chroma_db", embedding_function=embeddings) erneut, anstatt ihn aus Dokumenten neu aufzubauen. Chroma ist für die lokale Entwicklung praktisch; dieselbe LangChain-Schnittstelle steht vor gehosteten Speichern wie Pinecone oder pgvector, wenn Sie eine einzelne Maschine überwachsen, sodass sich der Rest Ihres Codes nicht ändert.
Schritt 4: Die Antwort abrufen und generieren
Mit vorhandenen Vektoren ist der Abfragepfad kurz. Wandeln Sie den Speicher in einen Retriever um, betten Sie die Frage des Nutzers ein, holen Sie die nächsten Chunks und übergeben Sie sie an ein Chat-Modell mit einem Prompt, der es anweist, nur aus dem bereitgestellten Kontext zu antworten. LangChains Ausdruckssprache verdrahtet diese zu einer Kette.
from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser retriever = vector_store.as_retriever(search_kwargs={"k": 4}) llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) prompt = ChatPromptTemplate.from_template( "Answer using only the context below.\n\n" "Context:\n{context}\n\nQuestion: {question}" ) def format_docs(docs): return "\n\n".join(d.page_content for d in docs) chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) answer = chain.invoke("What does the getting-started guide say about setup?") print(answer)
Das Setzen von k=4 ruft die vier relevantesten Chunks ab; erhöhen Sie den Wert für breite Fragen, senken Sie ihn für enge. Eine temperature von 0 hält das Modell am abgerufenen Text verankert, statt zu improvisieren. Da der Prompt die Antwort auf den bereitgestellten Kontext beschränkt, bleiben Antworten in dem verwurzelt, was Sie tatsächlich gecrawlt haben, und da jeder Chunk seine source-Metadaten trägt, können Sie Zitierungen anzeigen, indem Sie die abgerufenen Dokumente direkt mit retriever.invoke(question) inspizieren.
Die gesamte Pipeline ausführen
Setzen Sie die vier Schritte in einem einzigen Skript der Reihe nach zusammen und Sie haben eine vollständige Pipeline: load_page über Ihre URLs, aufteilen, in Chroma einbetten, dann die Kette aufbauen und aufrufen. Der erste Lauf crawlt und bettet ein, was einen Moment dauert; nachfolgende Läufe, die den persistierten Speicher wieder öffnen, antworten in deutlich unter einer Sekunde, weil die aufwändige Arbeit bereits erledigt ist. Fügen Sie weitere URLs zur Liste hinzu und führen Sie die Ingestion erneut aus, um das Wissen des Systems zu erweitern.
Von hier aus lässt sich dieselbe Struktur natürlich ausbauen. Planen Sie den Ingestion-Job so, dass er Quellen in einem bestimmten Rhythmus aktualisiert, lassen Sie ihn auf Sitemaps zeigen, um ganze Abschnitte zu crawlen, oder tauschen Sie Chroma gegen einen gehosteten Vektorspeicher aus, wenn Ihr Korpus wächst. Für Hochvolumen-Crawling können Sie die Beschaffung auf die asynchrone Crawling API verlagern oder sie von einem Agenten über das Web MCP steuern lassen, und alles über den Smart AI Proxy routen, wenn Sie IP-Rotation vor Ihrem eigenen Fetcher benötigen. Der Pipeline-Vertrag ändert sich nicht: sauberer Text rein, fundierte Antworten raus. Mehr zur Extraktionsseite davon finden Sie in wie KI-Datenextraktion funktioniert.
Wichtigste Erkenntnisse
- Beschaffung ist der Qualitätshebel. Sauberes Markdown aus der Crawling API schlägt rohes HTML, weil Boilerplate zu lautem Chunk-Rauschen wird, das das Retrieval ruiniert.
- Vier Stufen, klare Grenzen. Beschaffen, aufteilen, einbetten, abrufen und generieren, sodass Sie jede einzelne ändern können, ohne die anderen zu berühren.
-
Mit Überlapp chunken.
RecursiveCharacterTextSplitterbei ca. 1000 Zeichen mit 150 Überlapp hält Chunks kohärent und Tatsachen über Grenzen hinweg intakt. - Einmal ingesten, viele Male abfragen. Den Vektorspeicher persistieren, damit die aufwändige Einbettungsarbeit nur dann anfällt, wenn sich Quellen ändern.
- Das Modell verankern. Den Prompt auf abgerufenen Kontext beschränken und die Temperatur niedrig halten, damit Antworten in dem verwurzelt bleiben, was gecrawlt wurde.
- Quell-Metadaten mitführen. Jedes Dokument mit seiner URL taggen, damit die genauen Seiten hinter jeder Antwort zitiert werden können.
Häufig gestellte Fragen
Warum Crawlbase statt eines eingebauten LangChain-Web-Loaders verwenden?
Eingebaute Loader gehen davon aus, dass eine Seite bei einer einfachen Anfrage nutzbares HTML zurückgibt, was moderne Sites selten tun: Sie rendern Inhalt im Browser und blockieren automatisierten Traffic. Die Crawling API rendert die Seite hinter einer rotierenden Residential-IP und gibt sauberes Markdown zurück, sodass Ihre Dokumente echter Inhalt sind statt leere Hüllen oder Sperrseiten. Diese Sauberkeit verbessert direkt die Chunk-Qualität und die Retrieval-Genauigkeit.
Soll ich für eine RAG-Pipeline HTML oder Markdown anfordern?
Markdown. Übergeben Sie format=markdown, damit die Seite als lesbarer Fließtext mit erhaltenen Überschriften und Listen zurückkommt und die Navigation, Skripte und Werbung herausgefiltert werden. Diese strukturellen Hinweise helfen dem Splitter, an natürlichen Grenzen zu trennen, und das Entfernen von Boilerplate hält minderwertigen Text aus Ihrem Vektorspeicher. Fordern Sie HTML nur dann an, wenn Sie bestimmte Elemente mit Selektoren parsen müssen, statt die Seite einzubetten.
Wie gehe ich mit JavaScript-lastigen Seiten um?
Verwenden Sie ein JavaScript-Token und fügen Sie ajax_wait und page_wait zu den Optionen hinzu, die Sie an api.get übergeben. Die Crawling API rendert die Seite dann in einem echten Browser, wartet auf asynchronen Inhalt und gibt das fertige Markdown zurück. Da die Beschaffung in der Funktion load_page isoliert ist, hat das Aktivieren des Renderings keinen Einfluss auf Aufteilen, Einbetten oder Retrieval.
Welche Chunk-Größe und welchen Überlapp soll ich verwenden?
Beginnen Sie mit etwa 1000 Zeichen pro Chunk und 150 Zeichen Überlapp für allgemeinen Fließtext. Kleinere Chunks verbessern die Präzision bei dichtem technischen Inhalt; größere Chunks eignen sich für Langform-Artikel, wo Kontext über Absätze hinweggeht. Der Überlapp trägt etwas Kontext über Grenzen hinweg, damit eine zwischen zwei Chunks aufgeteilte Tatsache noch abrufbar ist. Behandeln Sie diese als Standardwerte und passen Sie sie anhand Ihrer eigenen Retrieval-Ergebnisse an.
Muss ich OpenAI für Einbettungen und das LLM verwenden?
Nein. Die Pipeline ist von Haus aus anbieterunabhängig. Tauschen Sie OpenAIEmbeddings und ChatOpenAI gegen jedes von LangChain unterstützte Einbettungs- und Chat-Modell aus, einschließlich lokaler Modelle, und der Code für Aufteilen, Speichern und Retrieval bleibt derselbe. Crawlbase sitzt vollständig auf der Beschaffungsseite, sodass Ihre Modellwahl nie beeinflusst, wie Daten abgerufen werden.
Wie halte ich die Wissensdatenbank aktuell?
Führen Sie die Ingestion-Stufen (beschaffen, aufteilen, einbetten) planmäßig gegen die URLs aus, die sich ändern, und öffnen Sie den persistierten Speicher für Abfragen dazwischen wieder. Bei großen oder häufig aktualisierten Korpora lassen Sie den Crawl auf Sitemaps zeigen und verlagern Sie die Beschaffung auf die asynchrone Scraper API, damit Sie viele Seiten ingesten können, ohne Ihre Anwendung zu blockieren.
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.

