Ohne KYC – keine ID, kein Selfie, keine Dokumente. Mit Krypto bezahlen: BTC, USDT, USDC, ETH, SOL, LTC
Sprache: Deutsch
API-Schlüssel erhalten

Docs · Python

Webseite in Markdown umwandeln in Python

Vier Rezepte, die die meisten Python-Projekte abdecken: eine Seite mit requests lesen, viele Seiten nebenläufig mit httpx lesen, eine Liste an einen Batch-Job übergeben und das Markdown für einen Vector Store in Chunks aufteilen.

Zuletzt aktualisiert: · 5 Min. Lesezeit

Kurze Antwort

Wie wandle ich eine Webseite in Markdown um in Python?

Rufen Sie WebReaderAPI mit requests.get("https://webreaderapi.com/api/v1/read", params={"url": ...}) und Ihrem Schlüssel im Header Authorization auf; die JSON-Antwort enthält den Hauptinhalt der Seite in markdown. Für viele Seiten nutzen Sie httpx.AsyncClient mit einem Semaphor oder einen Batch-Job. Eine Seite kostet 1 Credit, ab 0,07 $/1K Seiten (Stand Oktober 2026). Ohne KYC; mit Krypto bezahlen.

Was muss ich installiert haben?

Python 3.9 oder neuer und einen HTTP-Client. requests reicht für Skripte, die eine Seite nach der anderen lesen; httpx bringt Async-Unterstützung für viele Seiten parallel. Kein Paket von uns ist nötig: Die API ist reines HTTPS und JSON.

Halten Sie Ihren Schlüssel aus dem Code heraus. Lesen Sie ihn aus der Umgebungsvariable WEBREADER_API_KEY, wie es jedes Beispiel unten tut, und geben Sie jedem Projekt einen eigenen Schlüssel, damit Sie einen widerrufen können, ohne die anderen zu berühren.

Installation
pip install requests httpx
export WEBREADER_API_KEY="your-key"

Wie lese ich eine Seite mit requests?

Senden Sie url und alle Optionen als Query-Parameter, lösen Sie bei einem HTTP-Fehler eine Exception aus und lesen Sie die benötigten Felder. Das Beispiel behält den Hauptinhalt, begrenzt ihn für ein Modell auf 20.000 Zeichen und gibt aus, was der Aufruf gekostet hat.

requests: eine Seite
import os

import requests

API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


def read(url, **options):
    resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
    if resp.status_code != 200:
        error = resp.json()["error"]
        raise RuntimeError(error["code"] + ": " + error["message"])
    return resp.json()


page = read("https://example.com/docs/getting-started", max_chars=20000)
print(page["title"], page["word_count"], "words", page["credits_used"], "credit(s)")
print(page["markdown"][:500])

Wie lese ich viele Seiten gleichzeitig mit httpx?

Verwenden Sie einen httpx.AsyncClient und ein asyncio.Semaphore, das auf oder unter Ihrem Concurrency-Limit liegt. Jedes Konto hat ein Limit dafür, wie viele Seiten es gleichzeitig liest; Ihr Dashboard zeigt es an, und der Support kann es erhöhen. Fehler bei einer Seite sollten die anderen nicht stoppen, daher sammelt das Beispiel sie neben den Ergebnissen, statt eine Exception auszulösen.

Wenn Sie das Limit überschreiten, antwortet die API mit 429 concurrency_limit; nichts wird berechnet, aber die Seite wird nicht gelesen. Das Semaphor unter dem Limit zu halten ist einfacher als erneut zu versuchen.

httpx: bis zu 10 Seiten in Bearbeitung
import asyncio
import os

import httpx

API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


async def read_one(client, sem, url):
    async with sem:
        resp = await client.get(API, params={"url": url, "max_chars": 50000})
    body = resp.json()
    if resp.status_code == 200:
        return url, body, None
    return url, None, body["error"]["code"]


async def read_many(urls, limit=10):
    sem = asyncio.Semaphore(limit)
    async with httpx.AsyncClient(headers=HEADERS, timeout=75) as client:
        return await asyncio.gather(*(read_one(client, sem, u) for u in urls))


if __name__ == "__main__":
    urls = [f"https://example.com/blog/post-{n}" for n in range(1, 21)]
    for url, page, error in asyncio.run(read_many(urls)):
        print(url, "->", error or page["title"])

Wann sollte ich stattdessen einen Batch-Job verwenden?

Wenn Sie eine Liste mit bis zu 100 URLs haben und nicht jede Seite in dem Moment brauchen, in dem sie fertig ist. Ein Batch-Job läuft auf unserer Seite, sodass Ihr Skript keine Verbindungen offen hält und keine Retry-Logik ausführt. Starten Sie ihn mit POST https://webreaderapi.com/api/v1/batch und holen Sie die Ergebnisse dann mit der Cursor-Schleife ab, die auf der Seite zu Batch und Crawl gezeigt wird.

Für eine ganze Website starten Sie stattdessen einen Crawl und lassen ihn der Sitemap und den Links folgen; dieselbe Seite enthält eine vollständige Schleife, die eine .md-Datei pro Seite schreibt.

Batch-Job starten
import os

import requests

urls = [f"https://example.com/changelog/{year}" for year in range(2020, 2027)]
resp = requests.post(
    "https://webreaderapi.com/api/v1/batch",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"urls": urls, "options": {"format": "markdown", "main_content": True}},
    timeout=30,
)
resp.raise_for_status()
print("job:", resp.json()["job_id"])

Wie teile ich das Markdown für RAG in Chunks auf?

Teilen Sie zuerst an Überschriften und dann nach Größe. Überschriften markieren, wo Themen wechseln, daher kann ein Retriever einen Chunk, der an einer Überschrift beginnt, leichter zuordnen. Behalten Sie den Seitentitel und den Überschriftenpfad in den Metadaten jedes Chunks: Sie helfen dem Modell, die richtige Passage zu zitieren.

Die folgende Funktion braucht keine zusätzliche Bibliothek. Sie schneidet an Überschriften von # bis ### und teilt dann jeden langen Abschnitt an Leerzeilen auf, um unter einem Zeichenbudget zu bleiben. Speichern Sie final_url mit jedem Chunk, damit zwei Adressen, die zur selben Seite führen, keine Duplikate erzeugen.

Markdown an Überschriften aufteilen
import re


def chunk_markdown(page, max_chars=2000):
    """Split a /read response into chunks with a heading path and the source URL."""
    chunks, path, buf = [], [], []

    def flush():
        text = "\n".join(buf).strip()
        while text:
            part = text[:max_chars]
            cut = part.rfind("\n\n")
            if len(text) > max_chars and cut > max_chars // 2:
                part = part[:cut]
            chunks.append({"text": part.strip(), "section": " > ".join(path), "title": page.get("title"), "source": page["final_url"]})
            text = text[len(part):].strip()
        buf.clear()

    for line in page["markdown"].splitlines():
        m = re.match(r"^(#{1,3}) (.+)", line)
        if m:
            flush()
            level = len(m.group(1))
            path[:] = path[: level - 1] + [m.group(2).strip()]
        buf.append(line)
    flush()
    return chunks

Wie viel kostet ein Python-Job?

Zählen Sie Seiten, nicht Requests. 1 Credit = 1 gelesene Seite ohne Browser; 2 Credits, wenn die Seite einen Browser braucht, um JavaScript auszuführen. Eine Suchanfrage, die fehlschlägt oder die eine robots.txt einer Website verweigert, wird nicht abgerechnet. Das Lesen von 2.000 Dokumentationsseiten ohne Browser verbraucht etwa 2.000 Credits, was 10.000 Seiten (3,75 $) fünfmal abdeckt. Prepaid-Credits verfallen nie: Ungenutzte Seiten bleiben auf Ihrem Konto, bis Sie sie verwenden, sodass eine Pipeline, die einmal im Monat läuft, nur verbraucht, was sie liest.

Python: häufige Fragen

Gibt es ein offizielles Python-Paket?

Nein. Der Code auf dieser Seite ist alles, was Sie brauchen: WebReaderAPI ist eine einfache HTTPS-API, und requests oder httpx sprechen sie direkt an.

Wie gehe ich mit einer Seite um, die robots.txt verweigert?

Prüfen Sie den Fehlercode. blocked_by_robots und target_refused sind endgültig und kostenlos; protokollieren Sie sie und fahren Sie fort. Die Fehlerseite enthält einen Retry-Helfer, der nur die Codes erneut versucht, die sich ändern können.

Kann ich die API aus einem Jupyter-Notebook verwenden?

Ja. Das requests-Rezept funktioniert unverändert. In einem Notebook ersetzt await read_many(urls) das asyncio.run, da das Notebook bereits eine Event-Loop ausführt.

Welches Timeout sollte mein Client verwenden?

Etwas mehr als das der API selbst: Der Standard ist 30 Sekunden und das Maximum 60, daher lässt 75 Sekunden auf Client-Seite Raum für eine langsame Website und die Antwort.

Ab 0,07 $/1K Seiten · ohne KYC

API-Schlüssel erhalten