# Webseite in Markdown umwandeln in Python

> Webseiten in Markdown umwandeln in Python: requests, async httpx mit Concurrency-Limit, Batch-Jobs und RAG-Chunking. Ab 0,07 $/1K Seiten, ohne KYC.

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

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.

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

```bash
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*

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

```python
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](https://webreaderapi.com/de/docs/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*

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

```python
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](https://webreaderapi.com/de/docs/errors/) 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.

---

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.
