84 estrellas en GitHub y la cifra sigue aumentando. Yozh Crawler + Scraper es gratuito, de código abierto y se desarrolla de forma pública; danos una estrella si se gana un hueco en tu pila de herramientas.
CyberYozh Data / Yozh Crawler
Software · Yozh Crawler

Con una sola URL de origen, se transmitió todo el sitio web

Yozh Crawler rastrea un sitio web a partir de una única URL y transmite todas las páginas descubiertas a través de SSE. Se encarga de la parte más compleja del rastreo —frontera, deduplicación, alcance, cortesía y reintentos—, mientras que cada recuperación de página pasa por Yozh Scraper. Sin duplicación de Playwright, sin SaaS, se ejecuta en :8001.

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

Rastreo

Yozh Crawler recorre un sitio web a partir de una única URL inicial y transmite todas las páginas descubiertas a través de SSE. Se encarga de todo el proceso de descubrimiento —frontera, deduplicación, alcance, extracción de enlaces, reintentos y cortesía—, mientras que la obtención de cada página se delega a Yozh Scraper a través de HTTP. Solo hay que gestionar una única pila de Playwright, y todas las funciones del scraper (proxy, modo sigiloso, sesiones, reglas de extracción) se aplican también a las páginas rastreadas.

  • Predicados de ámbitosame-domain / subdomains / all / regex, con patrones de inclusión y exclusión y max_depth / max_pages límites máximos.
  • Deduplicación por «huella digital»: las URL se canonicizan y, a continuación, se someten a un algoritmo hash (SHA1), de modo que las consultas reordenadas y las variantes con barra al final se agrupan en una sola visita.
  • Método «round-robin» por dominio: un servidor lento o de gran tamaño no puede privar a los demás de trabajadores.
  • Limitador de velocidad adaptativo: un «token bucket» global por dominio (con fluctuación) compartido entre los distintos trabajos; un 429 reduce a la mitad el RPS durante un periodo de espera.
  • Estado de la sesión — Puntuación al estilo Crawlee: 401/403/429 cerrar una sesión; los errores aumentan su puntuación, mientras que el éxito la reduce; rotar cuando la puntuación sea ≥ 3 o tras 50 usos.
  • Transmisión SSE: cada trabajo emite stats, page, page_errory eventos de terminal done / cancelled . Actúa ante los resultados a medida que se van recibiendo.
URL basehttp://localhost:8001
Documentación de OpenAPIhttp://localhost:8001/docs
Punto final de MCPhttp://localhost:8001/mcp
Servicio complementario. El crawler delega todas las peticiones a Yozh Scraper, así que pon ambos en marcha a la vez: el scraper en :8000 y el rastreador en :8001.

Inicio rápido

El rastreador está conectado a la raíz docker-compose.yml y se ejecuta junto con el scraper:

bash
docker compose up --build
# scraper  → http://localhost:8000
# crawler  → http://localhost:8001

O bien, ejecútalo localmente sin Docker: dirígete SCRAPER_URL a un scraper en ejecución:

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

Uso básico

Enviar un rastreo con un seed_url y un scope. La llamada devuelve un job_id de inmediato; a continuación, el rastreo se ejecuta en segundo plano a través de tareas de trabajo.

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
  }'

La respuesta es simplemente el identificador de la tarea:

JSON
{ "job_id": "crawl_abc123" }

Desde aquí puedes ver eventos en directo o consultar el trabajo para obtener el registro completo.

Descubrimiento frente a recolección: el enable_scraping alternar

El mismo motor de rastreo funciona en ambos sentidos. Un valor booleano permite elegir entre un mapa de descubrimiento ligero y una recopilación con carga completa, y cada modo utiliza su propia configuración de proxy, por lo que no hay interferencias entre ellos.

enable_scraping: false

Mapa de exploración

Un escaneo ligero. El rastreador sigue procesando cada página (por lo que se detectan los enlaces creados con JavaScript), pero solo conserva la estructura básica, lo que resulta ideal para mapas de sitio, auditorías de enlaces y detección de cambios.

  • Mantiene url · parent_url · depth · status · took_ms
  • Incluye código HTML sin formato, capturas de pantalla y datos extraídos
  • Utiliza el más barato crawl_proxy
  • Cargas útiles mínimas, desplazamientos más rápidos
enable_scraping: true

Cosecha completa

Cada página visitada se guarda con su ScrapeResponse — HTML sin procesar, captura de pantalla opcional y cualquier campo de tus reglas de extracción. Rastrea y estructura en una sola pasada.

  • Mantiene el contenido completo ScrapeResponse por página
  • raw_html · screenshot · extraído data
  • Usos scrape_options.proxy_*
  • Aceptar extract reglas para obtener un JSON limpio
Selección de proxy. crawl_proxy Solo se utiliza cuando enable_scraping=false; scrape_options.proxy_* se utiliza cuando true. Si crawl_proxy es null, el scrape_options se utiliza el proxy independientemente del modo.

Ámbito del rastreo

El ámbito delimita el rastreo: dónde se permite que se desplace y hasta qué punto se permite que ejerza presión sobre cada host. Es el scope objeto de la solicitud.

CampoTipoPor defectoDescripción
modesame-domain · subdomains · all · regexsame-domain¿Qué enlaces están incluidos en el ámbito de aplicación? regex coincide con include_patterns.
include_patternsstring[][]Patrones de expresiones regulares con los que debe coincidir una URL para ser incluida en la cola (utilizados por regex el modo / como lista de permitidos).
exclude_patternsstring[][]Patrones de expresiones regulares que descartan una URL aunque, por lo demás, esté dentro del ámbito.
max_depthint3Profundidad máxima de los enlaces desde el nodo inicial (el nodo inicial tiene profundidad 0).
max_pagesint500Límite máximo de páginas visitadas antes de que finalice el rastreo.
per_domain_rpsfloat1.0Tasa de recarga del «token bucket» por dominio (solicitudes por segundo).
per_domain_concurrencyint1Número máximo de solicitudes simultáneas a un único dominio.
Ámbito «subdomains» simplista. Utiliza las dos últimas etiquetas como dominio registrable, por lo que produce coincidencias excesivas en hosts de la Lista de sufijos públicos como github.io o co.uk. Es preferible same-domain o regex para esos casos.

Resultados de streaming (SSE)

Cada tarea expone un flujo de eventos enviados por el servidor. Léalos para actuar sobre las páginas en el momento en que se detecten, en lugar de esperar a que finalice el rastreo.

cURL
curl -N http://localhost:8001/api/v1/crawl/crawl_abc123/events

El flujo emite cinco tipos de eventos:

EventoCuándo
statsResumen periódico del estado de avance (visitados / en cola / fallidos / omitidos por deduplicación / fuera del ámbito / reintentos).
pageUno por cada URL visitada. Contiene los metadatos completos ScrapeResponse en el modo de recopilación, y metadatos reducidos en el modo de descubrimiento.
page_errorUna URL que ha dado error tras todos los intentos de reconexión.
doneTerminal: el proceso de rastreo ha finalizado con normalidad.
cancelledTerminal: se ha cancelado el rastreo.

Un page evento en modo de descubrimiento tiene este aspecto:

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
}

Situación y resultados

¿Prefieres realizar consultas? El registro del trabajo se puede consultar en cualquier momento —incluso durante el rastreo—, con las páginas descubiertas hasta ese momento. /results es un alias del punto final del trabajo, que se mantiene por simetría con el rastreador.

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 se desplaza a través de queuedrunningdone (o failed / cancelled).

Cancelación de un rastreo

Detén un trabajo en ejecución con un DELETE. La cancelación suave es la opción predeterminada y la más 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"
Una cancelación forzada deja al rastreador «huérfano». Una ?hard=true cancela la solicitud en curso del rastreador, pero el scraper no tiene forma de saberlo, ya que la representación de la página finaliza por su parte. Prefiere la cancelación suave a menos que tengas que detenerlo inmediatamente.

MCP

fastapi-mcp está montado en /mcp como un punto final HTTP de Streamable. Apunta Claude o Cursor hacia él y las herramientas aparecerán automáticamente:

  • health
  • create_crawl
  • get_crawl
  • get_crawl_results
  • cancel_crawl
~/.claude/settings.json
"open-crawler": {
  "type": "http",
  "url": "http://localhost:8001/mcp"
}
El punto final SSE stream_crawl_events se ha excluido deliberadamente de MCP, ya que las respuestas en streaming no son compatibles con una herramienta de tipo solicitud/respuesta.

Referencia de configuración

Solicitud — CrawlRequest

CampoTipoPor defectoDescripción
seed_urlcadena (URL)obligatorioLa única URL desde la que comienza el rastreo.
scopeCrawlScopedefaultsLímites y cortesía: véase «Ámbito de rastreo».
scrape_optionsScrapeOptionsdefaultsSe envía tal cual al rastreador por página (url se inserta).
crawl_proxyScrapeOptions · nulonullProxy barato utilizado en modo de descubrimiento. Solo proxy_type / proxy_pool_id / proxy_geo se leen.
enable_scrapingboolfalseConserva el ScrapeResponse completo por página (true) o solo los metadatos de descubrimiento (false).

Por página — ScrapeOptions (seleccionado)

CampoTipoPor defectoDescripción
proxy_typenone · mobile · res_static · res_rotating · dc_static · …noneConjunto de servidores proxy por los que pasan las rutas de obtención de páginas.
devicedesktop · mobiledesktopVentana de visualización / Perfil de agente de usuario.
renderbooltrueVisualizar con el navegador (necesario para los enlaces creados con JavaScript).
stealthbooltrueAplica medidas de refuerzo contra la detección.
wait_untildomcontentloaded · networkidledomcontentloadedCuando la página se considera lista.
screenshotboolfalseHaz una captura de pantalla (modo «harvest»).
extractExtractRule · nulonullReglas de campo CSS/XPath → estructuradas data por página.
session_idcadena · nulonullReutilizar una sesión de scraper autenticada; consulta «Rastreos autenticados».

Variables de entorno

VariablePor defectoNotas
SCRAPER_URLhttp://web-scraper:8000Scraper de upstream (utiliza el nombre del servicio de Docker dentro de la red de Compose).
WORKERS2Número de tareas de rastreo en paralelo.
QUEUE_MAXSIZE200Profundidad de la cola de trabajos pendientes.
JOB_TIMEOUT_MS3_600_000Límite de tiempo real para una tarea de rastreo.
SCRAPER_JOB_TIMEOUT_MS120_000Tiempo de espera de la solicitud del scraper por página.
MAX_RETRIES3Número de reintentos por solicitud en caso de fallo temporal.
RETRY_HTTP_CODES[408,429,500,502,503,504]Códigos de estado que activan un nuevo intento.
RETRY_BACKOFF_MAX30.0Límite superior del retardo exponencial (en segundos).
SESSION_MAX_ERROR_SCORE3.0Umbral de caducidad de la sesión.
SESSION_MAX_USAGE50Rotar una sesión tras N usos.
SESSION_BLOCKED_CODES[401,403,429]Códigos que cierran una sesión al instante.

Referencia de la API

MétodoRutaPropósito
POST/api/v1/crawlCrear un trabajo. Devuelve {"job_id":"…"}.
GET/api/v1/crawl/{id}Historial del trabajo: estado, estadísticas y páginas completadas hasta ahora.
GET/api/v1/crawl/{id}/resultsAlias del registro de trabajo.
GET/api/v1/crawl/{id}/eventsCurso de SSE (stats/page/page_error/done/cancelled).
DELETE/api/v1/crawl/{id}?hard=boolCancelar el trabajo (cancelación parcial o total).
GET/api/v1/healthSalud + accesibilidad del scraper.

Detalles importantes

Sin autenticación (v1). Ningún punto final está autenticado; está diseñado para redes internas o de confianza. Cualquier persona que pueda acceder a él podría enviar una solicitud POST con una semilla arbitraria y convertirlo en un proxy SSRF. Manténlo dentro de tu VPC o detrás de tu propia puerta de enlace de autenticación.
robots.txt no se consulta. El rastreador recorre todas las URL incluidas en el ámbito, independientemente de /robots.txt. Eres responsable de respetar las reglas de los robots y las condiciones de uso de los sitios web de los que no eres propietario; utiliza exclude_patterns y los límites de frecuencia para mantener un comportamiento respetuoso.
No hay persistencia. Los trabajos se ejecutan en memoria y se reinician al reiniciar el contenedor (al igual que ocurre con el scraper). Para garantizar la durabilidad, transmite el flujo de SSE a tu propio almacén (un archivo, Postgres, Kafka) a medida que llegan las páginas. Reinicia periódicamente los procesos de larga duración, ya que los trabajos finalizados se acumulan en el almacén.
Rastreadores autenticados. Crea una sesión en el rastreador (POST /sessions + POST /sessions/{id}/login) y, a continuación, pasa scrape_options.session_id. El rastreador envía y recibe las cookies y el estado de almacenamiento en cada solicitud, por lo que todas las páginas rastreadas ven el estado autenticado.
Ejemplo en vivo

Mismo dominio — la opción segura por defecto

Permanece en el host semilla exacto, limita la profundidad y las páginas, y sé respetuoso. El modo de descubrimiento (enable_scraping: false) conserva solo el esqueleto de cada página.


            

Apreciado por los equipos de datos y los desarrolladores de IA

Lo que dicen quienes crean con Yozh

5,0 / 5 · Reseña de 5
GitHub
En una sola tarde cambiamos nuestro clúster interno de Playwright por Yozh. El punto final de MCP se integró directamente en nuestro agente Claude: sin necesidad de código de enlace, el rastreador y el extractor funcionaron a la primera.
Marcus Reinhardt Ingeniero jefe de datos Northwind Analytics
X
El sistema de preajustes es la característica estrella. Introducimos el nombre de una fuente y obtenemos un JSON limpio; la función de autocorrección incluso detectó dos cambios en el diseño de Amazon antes de que nos diéramos cuenta.
Priya Nair Fundador ScrapeStack
Reddit
Por fin, un rastreador de código abierto que trata los proxies y las sesiones como elementos de primer orden. Lo ejecutamos tras las barreras de inicio de sesión de los portales de nuestros socios: las sesiones se mantienen y los resultados son coherentes en todas las regiones.
Daniel Osei Ingeniero de backend Loopfeed
X
Lo he conectado a Cursor a través de MCP y ahora mi agente extrae datos web en tiempo real mientras realiza la tarea. El rastreo en tiempo real a través de SSE es justo lo que les faltaba a los flujos de trabajo de los agentes.
Elena Kovac Ingeniero de IA Vektor Labs
GitHub
Dejamos de utilizar una API de extracción de datos de pago para reducir costes y nos preparamos para un empeoramiento del servicio, pero ocurrió todo lo contrario. Es autohospedada, no hay facturación por solicitud y el esquema de salida es más claro que el que solíamos pagar.
Sofia Almeida Jefe de Ingeniería Tabbly
Código abierto · Licencia MIT · 84 ★

Introduce una URL. Observa cómo se carga la página.

Yozh Crawler se distribuye en el mismo repositorio que el scraper: uno docker compose up y ya tienes la detección y la extracción, listas para MCP, en tu propia infraestructura. Gratis. Para siempre. Márcanos con una estrella si se gana un hueco en tu pila de tecnologías.

Yozh Crawler + Scraper se distribuye bajo la licencia MIT. Úsalo. Haz un fork. Desarrolla con él.