# Página web para Markdown em Python

> Converta páginas web em Markdown em Python: requests, httpx assíncrono com limite de concorrência e jobs em lote. A partir de US$ 0,07/1K páginas, sem KYC.

- URL: https://webreaderapi.com/pt/docs/python/
- Última atualização: 2026-10-22
- Editor: WebReaderAPI (https://webreaderapi.com)

Quatro receitas que cobrem a maioria dos projetos Python: ler uma página com requests, ler muitas páginas em paralelo com httpx, entregar uma lista a um job em lote e dividir o Markdown em chunks para um vector store.

**Resposta rápida — Como converto uma página web em Markdown em Python?**

Chame **WebReaderAPI** com `requests.get("https://webreaderapi.com/api/v1/read", params={"url": ...})` e sua chave no header `Authorization`; a resposta JSON traz o conteúdo principal da página em `markdown`. Para muitas páginas, use `httpx.AsyncClient` com um semáforo ou um job em lote. Uma página custa 1 crédito, a partir de US$ 0,07/1K páginas em outubro de 2026. Sem KYC; pague com cripto.

## O que preciso ter instalado?

Python 3.9 ou superior e um cliente HTTP. `requests` basta para scripts que leem uma página por vez; `httpx` adiciona suporte assíncrono para muitas páginas em paralelo. Nenhum pacote nosso é necessário: a API é HTTPS e JSON puros.

Mantenha sua chave fora do código. Leia-a da variável de ambiente `WEBREADER_API_KEY`, como todos os exemplos abaixo fazem, e dê a cada projeto sua própria chave para revogar uma sem tocar nas outras.

*Instalar*

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

## Como leio uma página com requests?

Envie `url` e quaisquer opções como parâmetros de consulta, dispare em caso de erro HTTP e leia os campos de que precisa. O exemplo mantém o conteúdo principal, limita a 20.000 caracteres para um modelo e imprime quanto custou a chamada.

*requests: uma página*

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

```

## Como leio muitas páginas de uma vez com httpx?

Use um `httpx.AsyncClient` e um `asyncio.Semaphore` definido no seu limite de concorrência ou abaixo dele. Cada conta tem um limite de quantas páginas lê ao mesmo tempo; seu dashboard mostra esse limite, e o suporte pode aumentá-lo. Erros de uma página não devem parar as outras, então o exemplo os coleta ao lado dos resultados em vez de disparar.

Se você passar do limite, a API responde `429 concurrency_limit`; nada é cobrado, mas a página não é lida. Manter o semáforo abaixo do limite é mais simples do que tentar de novo.

*httpx: até 10 páginas em andamento*

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

```

## Quando devo usar um job em lote?

Quando você tem uma lista de até 100 URLs e não precisa de cada página no momento em que ela fica pronta. Um job em lote roda do nosso lado, então seu script não mantém conexões abertas nem faz lógica de retry. Inicie com `POST https://webreaderapi.com/api/v1/batch` e depois colete os resultados do job com o loop de cursor mostrado na [página de lote e rastreamento](https://webreaderapi.com/pt/docs/crawl/).

Para um site inteiro, inicie um rastreamento (crawl) e deixe que ele siga o sitemap e os links; a mesma página tem um loop completo que grava um arquivo `.md` por página.

*Iniciar um job de 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"])

```

## Como divido o Markdown em chunks para RAG?

Divida primeiro pelos títulos, depois pelo tamanho. Os títulos marcam onde os assuntos mudam, então um chunk que começa em um título é mais fácil para o retriever casar. Mantenha o título da página e o caminho de títulos nos metadados de cada chunk: eles ajudam o modelo a citar a seção certa.

A função abaixo não precisa de nenhuma biblioteca extra. Ela corta nos títulos de `#` a `###` e depois divide qualquer seção longa em linhas em branco para ficar dentro de um limite de caracteres. Armazene `final_url` com cada chunk, para que dois endereços que levam à mesma página não gerem duplicatas.

*Dividir Markdown por títulos*

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

```

## Quanto custa um job em Python?

Conte páginas, não requisições. 1 crédito = 1 página lida sem navegador; 2 créditos quando a página precisa de um navegador para renderizar JavaScript. Uma requisição que falha, ou que o robots.txt de um site recusa, não é cobrada. Ler 2.000 páginas de documentação sem navegador usa cerca de 2.000 créditos, o que 10.000 páginas (US$ 3,75) cobre cinco vezes. Os créditos pré-pagos nunca expiram: as páginas não usadas ficam na sua conta até você usá-las, então um pipeline que roda uma vez por mês usa apenas o que lê.

## Python: perguntas frequentes

### Existe um pacote oficial de Python?

Não. O código nesta página é tudo o que você precisa: WebReaderAPI é uma API HTTPS simples, e `requests` ou `httpx` falam com ela diretamente.

### Como trato uma página que o robots.txt recusa?

Verifique o código de erro. `blocked_by_robots` e `target_refused` são definitivos e gratuitos; registre-os e continue. A [página de erros](https://webreaderapi.com/pt/docs/errors/) tem um helper de retry que só repete os códigos que podem mudar.

### Posso usar a API a partir de um notebook Jupyter?

Sim. A receita com `requests` funciona como está. Em um notebook, `await read_many(urls)` substitui `asyncio.run`, porque o notebook já roda um event loop.

### Qual timeout meu cliente deve usar?

Um pouco mais que o da própria API: o padrão é 30 segundos e o máximo 60, então 75 segundos do lado do cliente deixa margem para um site lento e a resposta.

---

WebReaderAPI: O WebReaderAPI é uma API de leitor web para agentes de IA que converte qualquer página web pública em Markdown limpo, pago com cripto e sem envio de documentos. Os créditos pré-pagos a partir de US$ 0,07/1K páginas nunca expiram, e o robots.txt é respeitado. Sem KYC — sem documento, sem selfie, sem envio de arquivos. Pague com cripto.
