# Konwersja strony internetowej do Markdown w Pythonie

> Konwertuj strony do Markdown w Pythonie: requests i asynchroniczny httpx z limitem współbieżności. Od 0,07 USD/1K stron, bez KYC.

- URL: https://webreaderapi.com/pl/docs/python/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: WebReaderAPI (https://webreaderapi.com)

Cztery przepisy, które pokrywają większość projektów w Pythonie: odczytaj jedną stronę przez requests, odczytaj wiele stron współbieżnie przez httpx, przekaż listę do zadania wsadowego i podziel Markdown na fragmenty do vector store.

**Szybka odpowiedź — Jak przekonwertować stronę internetową do Markdown w Pythonie?**

Wywołaj **WebReaderAPI** z `requests.get("https://webreaderapi.com/api/v1/read", params={"url": ...})` i swoim kluczem w nagłówku `Authorization`; odpowiedź JSON zawiera główną treść strony w `markdown`. Do wielu stron użyj `httpx.AsyncClient` z semaforem lub zadania wsadowego. Jedna strona kosztuje 1 kredyt, od 0,07 USD/1K stron według stanu na październik 2026. Bez KYC; płatność kryptowalutą.

## Co muszę mieć zainstalowane?

Python 3.9 lub nowszy i jednego klienta HTTP. `requests` wystarcza do skryptów, które czytają po jednej stronie; `httpx` dodaje obsługę async do wielu stron równolegle. Żaden nasz pakiet nie jest potrzebny: API to zwykły HTTPS i JSON.

Trzymaj klucz poza kodem. Czytaj go ze zmiennej środowiskowej `WEBREADER_API_KEY`, jak w każdym przykładzie poniżej, i dawaj każdemu projektowi własny klucz, żeby móc unieważnić jeden bez ruszania pozostałych.

*Instalacja*

```bash
pip install requests httpx
export WEBREADER_API_KEY="your-key"
```

## Jak odczytać jedną stronę przez requests?

Wyślij `url` i dowolne opcje jako parametry zapytania, zgłoś wyjątek przy błędzie HTTP i odczytaj potrzebne pola. Przykład zachowuje główną treść, ogranicza ją do 20 000 znaków dla modelu i wypisuje, ile kosztowało wywołanie.

*requests: jedna strona*

```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])

```

## Jak odczytać wiele stron naraz przez httpx?

Użyj jednego `httpx.AsyncClient` i `asyncio.Semaphore` ustawionego na wartość równą lub poniżej limitu współbieżności. Każde konto ma limit liczby stron czytanych jednocześnie; Twój dashboard go pokazuje, a support może go podnieść. Błędy jednej strony nie powinny zatrzymywać pozostałych, więc przykład zbiera je obok wyników zamiast zgłaszać wyjątek.

Jeśli przekroczysz limit, API odpowiada `429 concurrency_limit`; nic nie jest rozliczane, ale strona nie zostaje odczytana. Trzymanie semafora poniżej limitu jest prostsze niż ponawianie.

*httpx: do 10 stron w locie*

```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"])

```

## Kiedy zamiast tego użyć zadania wsadowego?

Gdy masz listę do 100 URL-i i nie potrzebujesz każdej strony w momencie, gdy jest gotowa. Zadanie wsadowe działa po naszej stronie, więc Twoja skrypt nie trzyma otwartych połączeń i nie wykonuje logiki ponawiania. Uruchom je przez `POST https://webreaderapi.com/api/v1/batch`, a następnie odbierz wyniki zadania pętlą kursora pokazaną na [stronie o zadaniach wsadowych i crawlingu](https://webreaderapi.com/pl/docs/crawl/).

Dla całej witryny uruchom zamiast tego crawling i pozwól mu podążać za sitemapą i linkami; ta sama strona zawiera pełną pętlę, która zapisuje jeden plik `.md` na stronę.

*Uruchom zadanie batch*

```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"])

```

## Jak podzielić Markdown na fragmenty do RAG?

Najpierw dziel po nagłówkach, potem po rozmiarze. Nagłówki wyznaczają miejsca zmiany tematu, więc fragment zaczynający się od nagłówka łatwiej dopasować retrieverowi. Zachowaj tytuł strony i ścieżkę nagłówków w metadanych każdego fragmentu: pomagają modelowi zacytować właściwą sekcję.

Poniższa funkcja nie wymaga dodatkowej biblioteki. Dzieli na nagłówkach od `#` do `###`, a następnie dzieli każdą długą sekcję po pustych wierszach, aby zmieścić się w limicie znaków. Przechowuj `final_url` z każdym fragmentem, aby dwa adresy prowadzące do tej samej strony nie tworzyły duplikatów.

*Dziel Markdown po nagłówkach*

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

```

## Ile kosztuje zadanie w Pythonie?

Licz strony, nie zapytania. 1 kredyt = 1 odczytana strona bez przeglądarki; 2 kredyty, gdy strona wymaga przeglądarki do wyrenderowania JavaScript. Zapytanie, które się nie powiedzie lub które zostanie odrzucone przez robots.txt witryny, nie jest obciążane opłatą. Odczyt 2 000 stron dokumentacji bez przeglądarki zużywa około 2 000 kredytów, co 10 000 stron (3,75 USD) pokrywa pięciokrotnie. Przedpłacone kredyty nigdy nie wygasają: niewykorzystane strony pozostają na Twoim koncie, dopóki ich nie wykorzystasz, więc pipeline uruchamiany raz w miesiącu zużywa tylko to, co odczytuje.

## Python: najczęstsze pytania

### Czy istnieje oficjalny pakiet Python?

Nie. Kod na tej stronie to wszystko, czego potrzebujesz: WebReaderAPI to zwykłe API HTTPS, a `requests` lub `httpx` komunikują się z nim bezpośrednio.

### Jak obsłużyć stronę, której odmawia robots.txt?

Sprawdź kod błędu. `blocked_by_robots` i `target_refused` są ostateczne i darmowe; zapisz je i kontynuuj. [Strona błędów](https://webreaderapi.com/pl/docs/errors/) zawiera pomocnik ponawiania, który ponawia tylko kody, które mogą się zmienić.

### Czy mogę używać API z notatnika Jupyter?

Tak. Przepis `requests` działa bez zmian. W notatniku `await read_many(urls)` zastępuje `asyncio.run`, ponieważ notatnik już uruchamia pętlę zdarzeń.

### Jakiego limitu czasu powinien używać mój klient?

Trochę większego niż samo API: domyślnie 30 sekund, maksymalnie 60, więc 75 sekund po stronie klienta zostawia zapas na wolną stronę i odpowiedź.

---

WebReaderAPI: WebReaderAPI to API czytnika stron internetowych dla agentów AI, które zamienia każdą publiczną stronę internetową w czysty Markdown, z płatnością kryptowalutą i bez przesyłania dokumentów tożsamości. Przedpłacone kredyty od 0,07 USD/1K stron nigdy nie wygasają, a robots.txt jest respektowany. Bez KYC — bez dowodu, bez selfie, bez dokumentów. Płatność kryptowalutą.
