Kurze Antwort
Wie lade ich Webseiten in LangChain oder LlamaIndex als sauberes Markdown?
Rufen Sie den /read-Endpoint von WebReaderAPI auf und packen Sie jede Antwort in ein Document: das Markdown als Content, final_url und den Titel als Metadaten. Für Agenten exponieren Sie denselben Aufruf als read_url-Tool. Seiten kosten je 1 Credit, 2 mit Browser, ab 0,07 $/1K Seiten Stand Oktober 2026. Kein Paket von uns nötig. Ohne KYC; mit Krypto bezahlen.
Warum Markdown-Dokumente statt rohem HTML?
Weil Retrieval auf Text arbeitet und Markdown die Struktur bewahrt, die zählt, und den Rest weglässt. Überschriften bleiben erhalten, sodass ein Splitter an Themenwechseln trennen kann. Listen und Tabellen bleiben erhalten, sodass Schritte und Spezifikationen zusammenbleiben. Navigation, Cookie-Banner und Anzeigen sind weg, sodass sie Ihre Embeddings nicht verunreinigen und keine Tokens verschwenden.
Die Metadaten kommen aus demselben Aufruf: final_url als Quelle zum Zitieren, title, published_at und language zum Filtern.
import os
import requests
API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}
class PageError(Exception):
pass
def read_page(url, **options):
resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
body = resp.json()
if resp.status_code != 200:
raise PageError(body["error"]["code"])
return body
Wie baue ich einen LangChain-Document-Loader?
Verwandeln Sie jede Seite in ein langchain_core.documents.Document und teilen Sie es dann anhand der Überschriften mit MarkdownHeaderTextSplitter aus dem Paket langchain-text-splitters. Jeder Chunk behält den Überschriftenpfad in seinen Metadaten, und Sie fügen die Quell-URL hinzu, damit jede Antwort auf ihre Seite zurückverweisen kann.
Übersprungene Seiten, etwa solche, die robots.txt ablehnt, lösen PageError mit dem Code aus; protokollieren Sie sie und machen Sie weiter.
from langchain_core.documents import Document
from langchain_text_splitters import MarkdownHeaderTextSplitter
from reader import PageError, read_page
splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[("#", "h1"), ("##", "h2"), ("###", "h3")])
def load_pages(urls):
chunks = []
for url in urls:
try:
page = read_page(url)
except PageError as exc:
print("skipped", url, exc)
continue
for part in splitter.split_text(page["markdown"]):
part.metadata.update({"source": page["final_url"], "title": page.get("title", "")})
chunks.append(part)
return chunks
docs = load_pages(["https://example.com/docs/install", "https://example.com/docs/configure"])
print(len(docs), "chunks", isinstance(docs[0], Document) if docs else "")
Wie gebe ich einem LangChain-Agenten ein read_url-Tool?
Dekorieren Sie eine Funktion mit @tool aus langchain_core.tools. Der Docstring wird zur Tool-Beschreibung, die das Modell liest, sagen Sie also, was das Tool zurückgibt und wann es zu verwenden ist. Geben Sie reinen Text zurück: eine Titelzeile, die Quelle und das Markdown, mit max_chars passend zu Ihrem Modell gekürzt.
Binden Sie das Tool an ein beliebiges Chat-Modell, das Tool Calling unterstützt, oder übergeben Sie es an Ihren Agent-Konstruktor. Das Modell ruft dann read_url mit einer URL aus der Konversation oder aus einem Suchergebnis auf.
import os
from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
from reader import PageError, read_page
@tool
def read_url(url: str, max_chars: int = 20000) -> str:
"""Read a public web page and return its main content as Markdown, with its title and source URL."""
try:
page = read_page(url, max_chars=max_chars)
except PageError as exc:
return "Could not read the page: " + str(exc)
return "# " + (page.get("title") or url) + "\nSource: " + page["final_url"] + "\n\n" + page["markdown"]
model = init_chat_model(os.environ.get("CHAT_MODEL", "openai:gpt-4.1-mini")).bind_tools([read_url])
reply = model.invoke("Summarise https://example.com/blog/first-post in three bullet points.")
print(reply.tool_calls)
Wie mache ich dasselbe in LlamaIndex?
LlamaIndex verwendet seine eigene Document-Klasse mit text und metadata sowie FunctionTool.from_defaults, um eine Funktion in ein Tool zu verwandeln. Der Helper bleibt gleich. Bauen Sie einen Index aus den Dokumenten mit VectorStoreIndex.from_documents, das das Embedding-Modell verwendet, das Ihre LlamaIndex-Einstellungen benennen.
import os
import time
import requests
from llama_index.core import Document, VectorStoreIndex
from llama_index.core.tools import FunctionTool
from reader import HEADERS, PageError, read_page
def crawl_pages(start_url, limit=200):
job = requests.post("https://webreaderapi.com/api/v1/crawl", headers=HEADERS, json={"url": start_url, "limit": limit}, timeout=30).json()
pages, cursor = [], None
while True:
params = {"cursor": cursor} if cursor else {}
res = requests.get("https://webreaderapi.com/api/v1/jobs/" + job["job_id"], headers=HEADERS, params=params, timeout=30).json()
pages += [r for r in res["results"] if "error" not in r]
cursor = res.get("next_cursor")
if not cursor and res["status"] == "done":
return pages
if not cursor:
time.sleep(5)
pages = crawl_pages("https://docs.example.com/")
docs = [Document(text=p["markdown"], metadata={"source": p["final_url"], "title": p.get("title", "")}) for p in pages]
index = VectorStoreIndex.from_documents(docs)
print(index.as_query_engine().query("How do I install the product?"))
def read_url(url: str, max_chars: int = 20000) -> str:
"""Read a public web page and return its main content as Markdown."""
try:
return read_page(url, max_chars=max_chars)["markdown"]
except PageError as exc:
return "Could not read the page: " + str(exc)
read_tool = FunctionTool.from_defaults(fn=read_url, name="read_url")
print(os.environ.get("CHAT_MODEL", "set CHAT_MODEL for your agent"), read_tool.metadata.name)
Wie groß muss ein Crawl für einen kleinen RAG-Index sein?
Kleiner, als Sie vielleicht denken. Die Dokumentation eines Produkts umfasst oft ein paar hundert Seiten, und ein Crawl mit include_paths auf den Docs-Abschnitt liest nur diese. Bei 1 Credit pro Seite kostet eine 300-seitige Docs-Website etwa 300 Credits; 10.000 Seiten (3,75 $) aktualisiert sie viele Male.
Crawlen Sie neu nach einem Zeitplan, der dazu passt, wie oft sich die Quelle ändert, und vergleichen Sie final_url und word_count mit dem vorherigen Lauf, um nur die geänderten Seiten neu zu embedden. Prepaid-Credits verfallen nie: Ungenutzte Seiten bleiben auf Ihrem Konto, bis Sie sie verwenden.
LangChain und LlamaIndex: häufige Fragen
Gibt es ein WebReaderAPI-Paket für LangChain oder LlamaIndex?
Nein. Der Helper und das Tool auf dieser Seite sind die gesamte Integration, Sie können sie also frei anpassen. WebReaderAPI ist eine einfache HTTPS-API, die jeder Python-HTTP-Client aufrufen kann.
Welche LangChain-Version ist dafür nötig?
Jede aktuelle mit langchain_core, langchain-text-splitters und init_chat_model. Der Tool-Decorator und die Klasse Document sind über aktuelle Releases hinweg stabil geblieben.
Wie vermeide ich, Seiten erneut zu lesen, die sich nicht geändert haben?
Speichern Sie final_url, word_count und das Datum jedes Lesevorgangs. Lesen Sie nach einem Zeitplan erneut, und embedden Sie eine Seite nur neu, wenn sich ihre Wortzahl oder ihr Content-Hash ändert. Jeder erneute Lesevorgang kostet einen Credit, wählen Sie also einen Rhythmus, der zur Quelle passt.
Was ist, wenn einige Seiten abgelehnt werden?
Sie lösen PageError mit einem Code wie blocked_by_robots aus, und sie kosten nichts. Wenn eine Website unsere Suchanfrage ablehnt, geben wir ihre Antwort zurück: Wir lösen nie CAPTCHAs, melden uns nie in Konten an und versuchen nie, eine Sperre oder eine Paywall zu umgehen.