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 ámbito —
same-domain/subdomains/all/regex, con patrones de inclusión y exclusión ymax_depth/max_pageslí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
429reduce a la mitad el RPS durante un periodo de espera. - Estado de la sesión — Puntuación al estilo Crawlee:
401/403/429cerrar 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 terminaldone/cancelled. Actúa ante los resultados a medida que se van recibiendo.
: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:
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:
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 -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:
{ "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.
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
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
ScrapeResponsepor página -
raw_html·screenshot· extraídodata - Usos
scrape_options.proxy_* - Aceptar
extractreglas para obtener un JSON limpio
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.
| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
mode | same-domain · subdomains · all · regex | same-domain | ¿Qué enlaces están incluidos en el ámbito de aplicación? regex coincide con include_patterns. |
include_patterns | string[] | [] | 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_patterns | string[] | [] | Patrones de expresiones regulares que descartan una URL aunque, por lo demás, esté dentro del ámbito. |
max_depth | int | 3 | Profundidad máxima de los enlaces desde el nodo inicial (el nodo inicial tiene profundidad 0). |
max_pages | int | 500 | Límite máximo de páginas visitadas antes de que finalice el rastreo. |
per_domain_rps | float | 1.0 | Tasa de recarga del «token bucket» por dominio (solicitudes por segundo). |
per_domain_concurrency | int | 1 | Número máximo de solicitudes simultáneas a un único dominio. |
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 -N http://localhost:8001/api/v1/crawl/crawl_abc123/events
El flujo emite cinco tipos de eventos:
| Evento | Cuándo |
|---|---|
stats | Resumen periódico del estado de avance (visitados / en cola / fallidos / omitidos por deduplicación / fuera del ámbito / reintentos). |
page | Uno por cada URL visitada. Contiene los metadatos completos ScrapeResponse en el modo de recopilación, y metadatos reducidos en el modo de descubrimiento. |
page_error | Una URL que ha dado error tras todos los intentos de reconexión. |
done | Terminal: el proceso de rastreo ha finalizado con normalidad. |
cancelled | Terminal: se ha cancelado el rastreo. |
Un page evento en modo de descubrimiento tiene este aspecto:
{
"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 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 se desplaza a través de queued → running → done (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.
# 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 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:
healthcreate_crawlget_crawlget_crawl_resultscancel_crawl
"open-crawler": {
"type": "http",
"url": "http://localhost:8001/mcp"
}
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
| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
seed_url | cadena (URL) | obligatorio | La única URL desde la que comienza el rastreo. |
scope | CrawlScope | defaults | Límites y cortesía: véase «Ámbito de rastreo». |
scrape_options | ScrapeOptions | defaults | Se envía tal cual al rastreador por página (url se inserta). |
crawl_proxy | ScrapeOptions · nulo | null | Proxy barato utilizado en modo de descubrimiento. Solo proxy_type / proxy_pool_id / proxy_geo se leen. |
enable_scraping | bool | false | Conserva el ScrapeResponse completo por página (true) o solo los metadatos de descubrimiento (false). |
Por página — ScrapeOptions (seleccionado)
| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
proxy_type | none · mobile · res_static · res_rotating · dc_static · … | none | Conjunto de servidores proxy por los que pasan las rutas de obtención de páginas. |
device | desktop · mobile | desktop | Ventana de visualización / Perfil de agente de usuario. |
render | bool | true | Visualizar con el navegador (necesario para los enlaces creados con JavaScript). |
stealth | bool | true | Aplica medidas de refuerzo contra la detección. |
wait_until | domcontentloaded · networkidle | domcontentloaded | Cuando la página se considera lista. |
screenshot | bool | false | Haz una captura de pantalla (modo «harvest»). |
extract | ExtractRule · nulo | null | Reglas de campo CSS/XPath → estructuradas data por página. |
session_id | cadena · nulo | null | Reutilizar una sesión de scraper autenticada; consulta «Rastreos autenticados». |
Variables de entorno
| Variable | Por defecto | Notas |
|---|---|---|
SCRAPER_URL | http://web-scraper:8000 | Scraper de upstream (utiliza el nombre del servicio de Docker dentro de la red de Compose). |
WORKERS | 2 | Número de tareas de rastreo en paralelo. |
QUEUE_MAXSIZE | 200 | Profundidad de la cola de trabajos pendientes. |
JOB_TIMEOUT_MS | 3_600_000 | Límite de tiempo real para una tarea de rastreo. |
SCRAPER_JOB_TIMEOUT_MS | 120_000 | Tiempo de espera de la solicitud del scraper por página. |
MAX_RETRIES | 3 | Nú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_MAX | 30.0 | Límite superior del retardo exponencial (en segundos). |
SESSION_MAX_ERROR_SCORE | 3.0 | Umbral de caducidad de la sesión. |
SESSION_MAX_USAGE | 50 | Rotar 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étodo | Ruta | Propósito |
|---|---|---|
| POST | /api/v1/crawl | Crear 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}/results | Alias del registro de trabajo. |
| GET | /api/v1/crawl/{id}/events | Curso de SSE (stats/page/page_error/done/cancelled). |
| DELETE | /api/v1/crawl/{id}?hard=bool | Cancelar el trabajo (cancelación parcial o total). |
| GET | /api/v1/health | Salud + accesibilidad del scraper. |
Detalles importantes
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.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.