Diseña un web crawler que escale a miles de millones de páginas
Un crawler web es un recorrido de grafo distribuido con una regla dura: cubrir miles de millones de páginas sin parecer nunca un ataque de denegación de servicio contra ningún sitio.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Diseña un web crawler que escale a miles de millones de páginas
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Empieza por las restricciones, no por los componentes
Antes de dibujar una sola caja, hay que fijar tres números, porque determinan cada decisión de diseño posterior: cuántas páginas hace falta tener rastreadas (digamos, 5 mil millones), qué tan frescas deben mantenerse (los sitios de noticias se vuelven a rastrear cada hora, los sitios estáticos tipo folleto cada mes) y qué tan educado hay que ser (no más de aproximadamente una petición cada 1–2 segundos a un mismo host, respetando robots.txt). Rastrear 5 mil millones de páginas al mes equivale a unas 2,000 peticiones por segundo sostenidas, repartidas entre todos los hosts que existen en la web abierta — y esa tasa de fetch se debe lograr sin concentrar más de un par de peticiones por segundo en ningún sitio individual, razón por la cual "distribuir el trabajo" y "limitar por host" tiran en direcciones opuestas, y todo el sistema está construido para reconciliar ambas cosas.
#El URL Frontier: el corazón del diseño
El frontier no es una sola cola — una FIFO única dejaría sin recursos a los hosts de baja prioridad o dejaría que un solo host dominara el pool de fetch. El diseño estándar (que viene de los crawlers estilo Mercator) tiene dos capas:
- Colas frontales — una por nivel de prioridad — ordenan las URLs por un puntaje de frescura/importancia (con qué frecuencia cambia la página, cuántas otras páginas enlazan hacia ella). Las URLs nuevas y las que se van a re-rastrear caen en la cola frontal que corresponde a su prioridad.
- Colas traseras — una por host — garantizan la cortesía. Un enrutador separado extrae de las colas frontales (con sesgo hacia mayor prioridad) y coloca cada URL en la cola trasera de su host. Un fetcher solo puede extraer de una cola trasera cuyo host no esté "en vuelo" y cuyo tiempo de enfriamiento por host ya haya pasado.
Esta separación es lo que permite que el crawler sea ambicioso con la prioridad de forma global, mientras es estrictamente cortés de forma local, por host.
#La cortesía no es opcional
El robots.txt se obtiene una vez por host, se cachea con un TTL, y se consulta antes de cada fetch a ese host — tanto para las rutas prohibidas como para un Crawl-delay explícito. Incluso cuando un sitio no especifica ningún delay, el crawler impone un piso razonable (comúnmente 1–2 segundos entre peticiones al mismo host) y hace backoff exponencial ante respuestas 429 o 5xx. El hash consistente del hostname a través de la flota de fetchers es lo que hace esto aplicable sin necesidad de un lock distribuido: toda URL de example.com se enruta al mismo nodo (o a un conjunto pequeño de nodos), de modo que el límite de tasa por host es un contador local en memoria y no un problema de coordinación distribuida.
#La deduplicación ocurre en dos niveles distintos
- Deduplicación a nivel de URL evita encolar la misma URL dos veces. Con miles de millones de URLs, un hash set con cada URL vista es demasiado grande para mantener en memoria — este es el caso de uso de producción canónico del filtro de Bloom de la lección anterior: una respuesta "definitivamente no vista" evita reencolar gratis, y el raro falso positivo solo significa que una página se rastrea con menos frecuencia de la debida, algo aceptable a esta escala.
- Deduplicación a nivel de contenido detecta páginas alcanzables por URLs distintas pero que en la práctica son el mismo documento — espejos, variantes con parámetros de tracking, páginas de plantilla repetidas. El hashing exacto (SHA-256 del cuerpo) solo detecta duplicados byte a byte; SimHash o MinHash en cambio producen una huella que se mantiene similar para documentos casi idénticos, de modo que páginas que solo difieren en una marca de tiempo, un banner o un token de sesión terminan colapsando en una sola entrada del índice.
La normalización de URL debe ocurrir antes de cualquiera de las dos deduplicaciones: pasar el host a minúsculas, quitar puertos por defecto, ordenar o eliminar parámetros de query de tracking, y resolver los segmentos . / .. — de lo contrario, la misma página bajo cuatro URLs cosméticamente distintas parece cuatro páginas diferentes.
#Tolerancia a fallos y caída de fetchers
El trabajo se reparte como arriendos (leases), no como asignaciones permanentes: un fetcher retira un lote de URLs de su cola trasera con un tiempo de expiración. Si el fetcher se cae o se cuelga, el lease expira y otro fetcher de la flota retoma las mismas URLs — ningún coordinador central tiene que notar la caída y reasignar el trabajo manualmente. Las URLs que fallan repetidamente (fallos de DNS, errores TLS, 5xx persistentes) se desvían a una cola de mensajes muertos con reintentos limitados y backoff exponencial, en vez de reintentarse para siempre consumiendo capacidad del frontier.
#Almacenamiento y planificación de frescura
El HTML crudo cae en almacenamiento de blobs, indexado por un hash de contenido, con un almacén de metadatos separado (URL, marca de tiempo del fetch, código HTTP, hash de contenido, enlaces salientes) en una base de columnas anchas o de documentos construida para alto throughput de escritura. El grafo de enlaces derivado de esos enlaces salientes alimenta tanto la planificación de frescura como el ranking de importancia: las páginas que cambian con frecuencia (detectado comparando rastreos sucesivos) se vuelven a encolar antes, y las páginas con muchos enlaces entrantes (una aproximación barata a la importancia estilo PageRank) reciben un impulso de prioridad en la cola frontal, para que el presupuesto de rastreo se gaste donde más importa.
| Componente | Responsabilidad | Desafío clave |
|---|---|---|
| URL Frontier | Ordenar y enrutar las próximas URLs a rastrear | Balancear la prioridad global con la cortesía por host |
| Resolutor DNS | Traducir hosts a IPs | Cachear — el DNS crudo a este volumen es un sumidero de latencia y costo |
| Pool de Fetchers | Emitir la petición HTTP real | Escalado horizontal, límite de tasa por host |
| Caché de robots.txt | Filtrar cada fetch contra la política del sitio | TTL y frescura de la propia caché por host |
| Dedup de URL (filtro de Bloom) | Saltar URLs ya encoladas | Memoria a miles de millones de URLs — de ahí lo probabilístico |
| Dedup de contenido (SimHash) | Saltar páginas casi duplicadas | El hashing exacto no detecta variantes cosméticas |
| Almacén de contenido crudo | Persistir las páginas obtenidas | Escala de petabytes, barato y durable |
| Grafo de enlaces / Índice | Alimentar el ranking y la planificación de frescura | Mantenerlo consultable a esta escala |