Rastreio
O Yozh Crawler percorre um site a partir de um único URL inicial e transmite todas as páginas descobertas através do SSE. É responsável pela descoberta — fronteira, deduplicação, âmbito, extração de links, novas tentativas, cortesia —, enquanto a recuperação de cada página é delegada ao Yozh Scraper através de HTTP. Há exatamente uma pilha do Playwright para operar, e todas as funcionalidades do scraper (proxy, modo furtivo, sessões, regras de extração) aplicam-se também às páginas rastreadas.
- Predicados de âmbito —
same-domain/subdomains/all/regex, com padrões de inclusão e exclusão emax_depth/max_pageslimites rígidos. - Desduplicação por «impressão digital» — Os URLs são canonizados e, em seguida, submetidos a um algoritmo de hash (SHA1), de modo a que as consultas reordenadas e as variantes com barra final sejam agrupadas numa única visita.
- Método «round-robin» por domínio — um host lento ou de grandes dimensões não pode privar os outros de trabalhadores.
- Limitador de taxa adaptativo — um «token bucket» global por domínio (com jitter) partilhado entre tarefas; um
429reduz para metade o RPS durante um período de arrefecimento. - Estado da sessão — Pontuação ao estilo Crawlee:
401/403/429encerrar uma sessão; os erros aumentam a sua pontuação, enquanto os sucessos a reduzem; alternar quando a pontuação for ≥ 3 ou após 50 utilizações. - Transmissão SSE — cada tarefa emite
stats,page,page_errore eventos de terminaldone/cancelled. Reaja às descobertas à medida que estas surgem.
:8000 e o rastreador no :8001.Início rápido
O rastreador está ligado à raiz docker-compose.yml e surge juntamente com o scraper:
docker compose up --build # scraper → http://localhost:8000 # crawler → http://localhost:8001
Ou execute-o localmente sem o Docker — selecione SCRAPER_URL para um scraper em execução:
cd yozh-crawler pip install -r requirements.txt cp .env.example .env # edit SCRAPER_URL if needed python -m uvicorn src.main:app --reload --port 8001
Utilização básica
Enviar um rastreio com um seed_url e um scope. A chamada devolve um job_id imediatamente; o rastreio é então executado em segundo plano através de tarefas de trabalho.
curl -X POST http://localhost:8001/api/v1/crawl
-H "Content-Type: application/json"
-d '{
"seed_url": "https://example.com",
"scope": {"mode": "same-domain", "max_depth": 2, "max_pages": 50,
"per_domain_rps": 1.0, "per_domain_concurrency": 1},
"scrape_options": {"proxy_type": "none"},
"enable_scraping": false
}'
A resposta é apenas o identificador da tarefa:
{ "job_id": "crawl_abc123" }
A partir daqui, pode transmitir eventos em direto ou consultar a tarefa para obter o registo completo.
Descoberta vs. colheita — o enable_scraping alternar
O mesmo motor de rastreamento funciona nos dois sentidos. Um valor booleano permite escolher entre um mapa de descoberta com baixo custo e uma recolha com carga útil completa — e cada modo utiliza a sua própria configuração de proxy, pelo que não há interferência entre eles.
Mapa de descoberta
Uma varredura leve. O scraper continua a renderizar cada página (para que os links criados em JS sejam detetados), mas o rastreador mantém apenas a estrutura básica — ideal para mapas do site, auditorias de links e deteção de alterações.
- Mantém
url·parent_url·depth·status·took_ms - Inserir HTML sem formatação, capturas de ecrã e dados extraídos
- Utiliza o mais barato
crawl_proxy - Cargas úteis mais pequenas, percursos mais rápidos
Colheita completa
Cada página visitada é guardada com o seu ScrapeResponse — HTML bruto, captura de ecrã opcional e quaisquer campos das suas regras de extração. Rastreie e estruture numa única etapa.
- Mantém o conteúdo completo
ScrapeResponsepor página -
raw_html·screenshot· extraídodata - Utilizações
scrape_options.proxy_* - Passar
extractregras para obter um JSON limpo
crawl_proxy é utilizada apenas quando enable_scraping=false; scrape_options.proxy_* é utilizado quando true. Se crawl_proxy for null, o scrape_options proxy é utilizado independentemente do modo.Âmbito do rastreio
O âmbito define os limites do rastreio: onde é permitido avançar e até que ponto é permitido sobrecarregar cada anfitrião. É o scope objeto na solicitação.
| Campo | Tipo | Predefinição | Descrição |
|---|---|---|---|
mode | same-domain · subdomains · all · regex | same-domain | Quais são os links que fazem parte do âmbito. regex corresponde a include_patterns. |
include_patterns | string[] | [] | Padrões de expressão regular com os quais um URL deve corresponder para ser colocado na fila (utilizados pelo regex modo / como lista de permissões). |
exclude_patterns | string[] | [] | Padrões de expressões regulares que excluem um URL, mesmo que este se encontre dentro do âmbito. |
max_depth | int | 3 | Profundidade máxima do link a partir do nó inicial (o nó inicial tem profundidade 0). |
max_pages | int | 500 | Limite máximo de páginas visitadas antes de o rastreio terminar. |
per_domain_rps | float | 1.0 | Taxa de recarga do «token-bucket» por domínio (pedidos/segundo). |
per_domain_concurrency | int | 1 | Número máximo de pedidos em curso para um único domínio. |
subdomains» simplista. Utiliza os dois últimos rótulos como domínio registável, pelo que resulta em correspondências excessivas em anfitriões da Lista de Sufixos Públicos, como github.io ou co.uk. Prefira same-domain ou regex para esses casos.Resultados de streaming (SSE)
Cada tarefa disponibiliza um fluxo de eventos enviados pelo servidor (Server-Sent Events). Leia-o para agir nas páginas assim que forem detetadas, em vez de esperar que o rastreio termine.
curl -N http://localhost:8001/api/v1/crawl/crawl_abc123/events
O fluxo emite cinco tipos de eventos:
| Evento | Quando |
|---|---|
stats | Resumo periódico do progresso (visitados / em fila / com falha / dedup_skipped / fora do âmbito / novas tentativas). |
page | Um por URL visitado. Contém os metadados completos ScrapeResponse no modo de recolha e metadados reduzidos no modo de descoberta. |
page_error | Um URL que falhou após todas as tentativas. |
done | Terminal — a execução terminou normalmente. |
cancelled | Terminal — o rastreio foi cancelado. |
Um page evento no modo de descoberta tem o seguinte aspeto:
{
"event": "page",
"url": "https://example.com/docs/intro",
"parent_url": "https://example.com/docs",
"depth": 2,
"status_code": 200,
"took_ms": 812,
"scrape_response": null
}
Situação e resultados
Prefere fazer uma consulta? O registo da tarefa pode ser consultado a qualquer momento — inclusive a meio do rastreio, com as páginas descobertas até ao momento. /results é um alias do endpoint da tarefa, mantido por uma questão de simetria com o scraper.
curl http://localhost:8001/api/v1/crawl/crawl_abc123 curl http://localhost:8001/api/v1/crawl/crawl_abc123/results
{
"job_id": "crawl_abc123",
"status": "running",
"stats": {
"visited": 47, "queued": 12, "failed": 0,
"dedup_skipped": 9, "out_of_scope": 31, "retries_total": 2
},
"pages": [
{ "url": "https://example.com/", "parent_url": null,
"depth": 0, "status_code": 200, "took_ms": 640 }
]
}
status passa por queued → running → done (ou failed / cancelled).Cancelar um rastreio
Interromper uma tarefa em execução com um DELETE. O cancelamento suave é a opção predefinida e a escolha mais segura.
# soft — stop scheduling, let in-flight requests drain curl -X DELETE "http://localhost:8001/api/v1/crawl/crawl_abc123?hard=false" # hard — abort in-flight asyncio tasks immediately curl -X DELETE "http://localhost:8001/api/v1/crawl/crawl_abc123?hard=true"
?hard=true cancela a solicitação em andamento do rastreador, mas o scraper não tem como saber disso — a renderização da página é concluída do seu lado. Prefira o cancelamento suave, a menos que seja necessário parar imediatamente.MCP
fastapi-mcp está montado em /mcp como um ponto final HTTP do Streamable. Basta apontar o Claude ou o Cursor para ele e as ferramentas aparecem automaticamente:
healthcreate_crawlget_crawlget_crawl_resultscancel_crawl
"open-crawler": {
"type": "http",
"url": "http://localhost:8001/mcp"
}
stream_crawl_events foi deliberadamente excluído do MCP — as respostas em streaming não se enquadram numa ferramenta de pedido/resposta.Referência de configuração
Pedido — CrawlRequest
| Campo | Tipo | Predefinição | Descrição |
|---|---|---|---|
seed_url | cadeia de caracteres (URL) | obrigatório | O URL único a partir do qual o rastreio tem início. |
scope | CrawlScope | defaults | Limites e cortesia — ver Âmbito do Crawl. |
scrape_options | ScrapeOptions | defaults | Enviado na íntegra para o scraper, página a página (url é inserido). |
crawl_proxy | ScrapeOptions · nulo | null | Proxy barato utilizado no modo de descoberta. Apenas proxy_type / proxy_pool_id / proxy_geo são lidos. |
enable_scraping | bool | false | Mantenha o texto completo ScrapeResponse por página (true) ou apenas os metadados de descoberta (false). |
Por página — ScrapeOptions (selecionado)
| Campo | Tipo | Predefinição | Descrição |
|---|---|---|---|
proxy_type | none · mobile · res_static · res_rotating · dc_static · … | none | Conjunto de proxies pelo qual as rotas de obtenção de páginas passam. |
device | desktop · mobile | desktop | Janela de visualização / Perfil do agente do utilizador. |
render | bool | true | Renderizar no navegador (necessário para links criados com JS). |
stealth | bool | true | Aplicar medidas de reforço contra a deteção. |
wait_until | domcontentloaded · networkidle | domcontentloaded | Quando a página for considerada pronta. |
screenshot | bool | false | Fazer uma captura de ecrã (modo de recolha). |
extract | ExtractRule · nulo | null | Regras de campo CSS/XPath → estruturadas data por página. |
session_id | cadeia de caracteres · nulo | null | Reutilizar uma sessão de scraper autenticada — consulte «Rastreios autenticados». |
Variáveis de ambiente
| Variável | Predefinição | Notas |
|---|---|---|
SCRAPER_URL | http://web-scraper:8000 | Scraper a montante (utilize o nome do serviço Docker na rede do Compose). |
WORKERS | 2 | Número de tarefas de rastreamento em paralelo. |
QUEUE_MAXSIZE | 200 | Profundidade da fila de tarefas pendentes. |
JOB_TIMEOUT_MS | 3_600_000 | Limite de tempo real para uma tarefa de rastreamento. |
SCRAPER_JOB_TIMEOUT_MS | 120_000 | Limite de tempo para cada pedido do scraper por página. |
MAX_RETRIES | 3 | Número de tentativas por pedido em caso de falha temporária. |
RETRY_HTTP_CODES | [408,429,500,502,503,504] | Códigos de estado que desencadeiam uma nova tentativa. |
RETRY_BACKOFF_MAX | 30.0 | Limite superior para o backoff exponencial (segundos). |
SESSION_MAX_ERROR_SCORE | 3.0 | Limite de expiração da sessão. |
SESSION_MAX_USAGE | 50 | Rotacionar uma sessão após N utilizações. |
SESSION_BLOCKED_CODES | [401,403,429] | Códigos que encerram uma sessão instantaneamente. |
Referência da API
| Método | Caminho | Finalidade |
|---|---|---|
| POST | /api/v1/crawl | Criar um trabalho. Resultados {"job_id":"…"}. |
| GET | /api/v1/crawl/{id} | Registo da tarefa — estado, estatísticas, páginas concluídas até ao momento. |
| GET | /api/v1/crawl/{id}/results | Alias do registo de trabalho. |
| GET | /api/v1/crawl/{id}/events | Ramo SSE (stats/page/page_error/done/cancelled). |
| DELETE | /api/v1/crawl/{id}?hard=bool | Cancelar o trabalho (cancelamento parcial ou total). |
| GET | /api/v1/health | Saúde + acessibilidade do raspador. |
Detalhes importantes
robots.txt não é consultado. O rastreador percorre todas as URLs abrangidas, independentemente de /robots.txt. É da sua responsabilidade respeitar as regras do robots.txt e os termos de utilização dos sites de destino que não lhe pertencem — utilize exclude_patterns e limites de taxa para agir de forma respeitosa.POST /sessions + POST /sessions/{id}/login) e, em seguida, passe scrape_options.session_id. O scraper troca cookies e o estado de armazenamento em cada pedido, pelo que todas as páginas rastreadas reconhecem o estado autenticado.