# LangChain und LlamaIndex: ein Web-Page-Loader und ein read_url-Tool

> Web-Page-Loader für LangChain und LlamaIndex: read_url-Tool, Document Loader und kleiner RAG-Index aus einem Crawl in Python. Ohne KYC, mit Krypto bezahlen.

- URL: https://webreaderapi.com/de/docs/langchain/
- Zuletzt aktualisiert: 2026-10-22
- Herausgeber: WebReaderAPI (https://webreaderapi.com)

Zwei Wege, die API in einem Python-AI-Stack zu nutzen: Seiten als Dokumente für Retrieval laden oder einem Agenten ein `read_url`-Tool geben. Dieselben 20 Zeilen Glue-Code dienen LangChain und LlamaIndex.

**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.

*reader.py: der gemeinsame Helper (speichern Sie ihn neben Ihrem Code)*

```python
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.

*LangChain: Seiten laden und teilen*

```python
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.

*LangChain: das read_url-Tool*

```python
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.

*LlamaIndex: einen Crawl indexieren, und ein read_url-Tool*

```python
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.

---

WebReaderAPI: WebReaderAPI ist eine Web-Reader-API für KI-Agenten, die jede öffentliche Webseite in sauberes Markdown umwandelt und die Sie mit Krypto bezahlen – ohne ID-Upload. Prepaid-Credits ab 0,07 $/1K Seiten verfallen nie, und robots.txt wird respektiert. Ohne KYC – keine ID, kein Selfie, keine Dokumente. Mit Krypto bezahlen.
