84 estrelas no GitHub e a contagem continua. O Yozh Crawler + Scraper é gratuito, de código aberto e desenvolvido publicamente — dá-nos uma estrela se ele merecer o seu lugar na tua pilha de ferramentas.
CyberYozh Data / Yozh Crawler
Software · Yozh Crawler

Bastou introduzir um URL de origem para que todo o site fosse transmitido

O Yozh Crawler percorre um site a partir de um único URL e transmite todas as páginas descobertas através do SSE. Ele encarrega-se da parte mais complexa do rastreamento — exploração de fronteiras, deduplicação, âmbito, comportamento adequado e tentativas de repetição —, enquanto cada recuperação de página passa pelo Yozh Scraper. Sem duplicação do Playwright, sem SaaS, funciona em :8001.

$curl -N :8001/api/v1/crawl -d '{"seed_url":"https://site.com", "scope":{"mode":"same-domain","max_depth":2,"max_pages":500}}'

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 âmbitosame-domain / subdomains / all / regex, com padrões de inclusão e exclusão e max_depth / max_pages limites 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 429 reduz para metade o RPS durante um período de arrefecimento.
  • Estado da sessão — Pontuação ao estilo Crawlee: 401/403/429 encerrar 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 terminal done / cancelled . Reaja às descobertas à medida que estas surgem.
URL de basehttp://localhost:8001
Documentação da OpenAPIhttp://localhost:8001/docs
Ponto final do MCPhttp://localhost:8001/mcp
Serviço complementar. O crawler delega todas as consultas ao Yozh Scraper, por isso, inicie ambos em simultâneo — o scraper no :8000 e o rastreador no :8001.

Início rápido

O rastreador está ligado à raiz docker-compose.yml e surge juntamente com o scraper:

bash
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:

bash
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
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:

JSON
{ "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.

enable_scraping: false

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
enable_scraping: true

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 ScrapeResponse por página
  • raw_html · screenshot · extraído data
  • Utilizações scrape_options.proxy_*
  • Passar extract regras para obter um JSON limpo
Seleção de proxy. 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.

CampoTipoPredefiniçãoDescrição
modesame-domain · subdomains · all · regexsame-domainQuais são os links que fazem parte do âmbito. regex corresponde a include_patterns.
include_patternsstring[][]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_patternsstring[][]Padrões de expressões regulares que excluem um URL, mesmo que este se encontre dentro do âmbito.
max_depthint3Profundidade máxima do link a partir do nó inicial (o nó inicial tem profundidade 0).
max_pagesint500Limite máximo de páginas visitadas antes de o rastreio terminar.
per_domain_rpsfloat1.0Taxa de recarga do «token-bucket» por domínio (pedidos/segundo).
per_domain_concurrencyint1Número máximo de pedidos em curso para um único domínio.
Âmbito «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
curl -N http://localhost:8001/api/v1/crawl/crawl_abc123/events

O fluxo emite cinco tipos de eventos:

EventoQuando
statsResumo periódico do progresso (visitados / em fila / com falha / dedup_skipped / fora do âmbito / novas tentativas).
pageUm por URL visitado. Contém os metadados completos ScrapeResponse no modo de recolha e metadados reduzidos no modo de descoberta.
page_errorUm URL que falhou após todas as tentativas.
doneTerminal — a execução terminou normalmente.
cancelledTerminal — o rastreio foi cancelado.

Um page evento no modo de descoberta tem o seguinte aspeto:

JSON
{
  "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
curl http://localhost:8001/api/v1/crawl/crawl_abc123
curl http://localhost:8001/api/v1/crawl/crawl_abc123/results
JSON
{
  "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 queuedrunningdone (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.

cURL
# 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"
O cancelamento definitivo deixa o scraper sem resposta. Um ?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:

  • health
  • create_crawl
  • get_crawl
  • get_crawl_results
  • cancel_crawl
~/.claude/settings.json
"open-crawler": {
  "type": "http",
  "url": "http://localhost:8001/mcp"
}
O SSE 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

CampoTipoPredefiniçãoDescrição
seed_urlcadeia de caracteres (URL)obrigatórioO URL único a partir do qual o rastreio tem início.
scopeCrawlScopedefaultsLimites e cortesia — ver Âmbito do Crawl.
scrape_optionsScrapeOptionsdefaultsEnviado na íntegra para o scraper, página a página (url é inserido).
crawl_proxyScrapeOptions · nulonullProxy barato utilizado no modo de descoberta. Apenas proxy_type / proxy_pool_id / proxy_geo são lidos.
enable_scrapingboolfalseMantenha o texto completo ScrapeResponse por página (true) ou apenas os metadados de descoberta (false).

Por página — ScrapeOptions (selecionado)

CampoTipoPredefiniçãoDescrição
proxy_typenone · mobile · res_static · res_rotating · dc_static · …noneConjunto de proxies pelo qual as rotas de obtenção de páginas passam.
devicedesktop · mobiledesktopJanela de visualização / Perfil do agente do utilizador.
renderbooltrueRenderizar no navegador (necessário para links criados com JS).
stealthbooltrueAplicar medidas de reforço contra a deteção.
wait_untildomcontentloaded · networkidledomcontentloadedQuando a página for considerada pronta.
screenshotboolfalseFazer uma captura de ecrã (modo de recolha).
extractExtractRule · nulonullRegras de campo CSS/XPath → estruturadas data por página.
session_idcadeia de caracteres · nulonullReutilizar uma sessão de scraper autenticada — consulte «Rastreios autenticados».

Variáveis de ambiente

VariávelPredefiniçãoNotas
SCRAPER_URLhttp://web-scraper:8000Scraper a montante (utilize o nome do serviço Docker na rede do Compose).
WORKERS2Número de tarefas de rastreamento em paralelo.
QUEUE_MAXSIZE200Profundidade da fila de tarefas pendentes.
JOB_TIMEOUT_MS3_600_000Limite de tempo real para uma tarefa de rastreamento.
SCRAPER_JOB_TIMEOUT_MS120_000Limite de tempo para cada pedido do scraper por página.
MAX_RETRIES3Nú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_MAX30.0Limite superior para o backoff exponencial (segundos).
SESSION_MAX_ERROR_SCORE3.0Limite de expiração da sessão.
SESSION_MAX_USAGE50Rotacionar 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étodoCaminhoFinalidade
POST/api/v1/crawlCriar 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}/resultsAlias do registo de trabalho.
GET/api/v1/crawl/{id}/eventsRamo SSE (stats/page/page_error/done/cancelled).
DELETE/api/v1/crawl/{id}?hard=boolCancelar o trabalho (cancelamento parcial ou total).
GET/api/v1/healthSaúde + acessibilidade do raspador.

Detalhes importantes

Sem autenticação (v1). Nenhum ponto de extremidade é autenticado — foi concebido para redes internas ou de confiança. Qualquer pessoa que consiga aceder-lhe pode enviar um pedido POST com uma semente arbitrária e transformá-lo num proxy SSRF. Mantenha-o dentro da sua VPC ou atrás do seu próprio gateway de autenticação.
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.
Sem persistência. Os trabalhos permanecem na memória e são reiniciados quando o contentor é reiniciado (da mesma forma que o scraper). Para garantir a durabilidade, transmita o fluxo do SSE para o seu próprio repositório (um ficheiro, Postgres, Kafka) à medida que as páginas vão chegando. Reinicie periodicamente os processos de longa duração — os trabalhos concluídos acumulam-se no repositório.
Rastreios autenticados. Crie uma sessão no scraper (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.
Exemplo prático

Mesmo domínio — a predefinição segura

Fique no host semente exato, limite a profundidade e as páginas, seja cortês. O modo de descoberta (enable_scraping: false) mantém apenas o esqueleto de cada página.


            

Apreciado pelas equipas de dados e pelos criadores de IA

O que dizem as pessoas que criam com o Yozh

5,0 / 5 · Crítica de 5
GitHub
Substituímos o nosso cluster interno do Playwright pelo Yozh numa tarde. O endpoint do MCP integrou-se diretamente no nosso agente Claude — sem qualquer código de ligação, o crawler e o scraper funcionaram logo.
Marcus Reinhardt Engenheiro de Dados Principal Northwind Analytics
X
O sistema de predefinições é a funcionalidade mais marcante. Passamos um nome de fonte e recebemos um JSON limpo em resposta; a correção automática chegou mesmo a detetar duas alterações no layout da Amazon antes de nos apercebermos.
Priya Nair Fundador ScrapeStack
Reddit
Finalmente, um scraper de código aberto que trata os proxies e as sessões como elementos de primeira classe. Executamo-lo em portais de parceiros que exigem início de sessão — as sessões mantêm-se ativas e os resultados permanecem consistentes em todas as regiões.
Daniel Osei Engenheiro de Backend Loopfeed
X
Liguei-o ao Cursor através do MCP e agora o meu agente obtém dados da Web em tempo real durante a execução da tarefa. O rastreio em tempo real através do SSE é exatamente o que faltava aos fluxos de trabalho do agente.
Elena Kovac Engenheiro de IA Vektor Labs
GitHub
Deixámos de utilizar uma API de extração de dados paga para reduzir custos e preparámo-nos para um declínio na qualidade — mas aconteceu o contrário. É auto-hospedada, não há cobrança por pedido e o esquema de saída é mais claro do que aquele pelo qual costumávamos pagar.
Sofia Almeida Gestor de Engenharia Tabbly
Código aberto · Licença MIT · 84 ★

Indique um URL. Veja o site a carregar

O Yozh Crawler vem incluído no mesmo repositório que o scraper — um docker compose up e já tens a descoberta + extração, pronta para MCP, na tua própria infraestrutura. Grátis. Para sempre. Marca-nos com uma estrela se o Yozh Crawler merecer o seu lugar na tua pilha de tecnologias.

O Yozh Crawler + Scraper é distribuído ao abrigo da licença MIT. Utilize-o. Faça um fork. Desenvolva com ele.