- Comparte la experiencia real de rastrear mil millones de páginas web en 24 horas y el proceso de diseño de un sistema moderno de web crawling
- Con hardware reciente e infraestructura en la nube, logró crawling a gran escala con un costo de unos cuantos cientos de dólares, y confirmó que el principal cuello de botella era el parsing
- Aunque solo se hizo parsing de HTML sin ejecutar JavaScript, todavía fue posible acceder a una cantidad considerable de páginas web
- Diseñó una arquitectura de clúster de nodos basada en Redis, maximizando la eficiencia con sharding por dominio y optimización de la estructura de procesos
- Se observó que los principales cuellos de botella no eran la red, sino la CPU, SSL y la memoria, y que la gestión del frontier de dominios grandes fue el tema clave
Definición del problema
- Se fijó la meta de rastrear mil millones de páginas web en 24 horas
- El presupuesto era de unos pocos cientos de dólares (al final, alrededor de 462 dólares), alineado con un caso de 2012
- Se recolectó solo HTML y se extrajeron únicamente enlaces
<a>, sin ejecutar JavaScript
- Se dio prioridad a la politeness (crawling responsable): cumplimiento de robots.txt, inclusión de información del User Agent, exclusión de dominios a solicitud, objetivo limitado al millón de dominios más populares y espera de 70 segundos dentro del mismo dominio, entre otras medidas
- Se aseguró tolerancia a fallos: reinicio ante fallas de nodos y aceptación de cierta pérdida parcial de datos, dado el enfoque basado en muestreo
Arquitectura y diseño
- A diferencia del estilo tradicional de entrevistas de diseño de sistemas existentes (distribución por función), se eligió una estructura donde cada nodo procesa por sí mismo todas las funciones (estado del crawling, parsing, fetching, almacenamiento, etc.)
- Se usaron 12 nodos, y cada nodo utilizó una instancia
i7i.4xlarge (16 vCPU, 128GB RAM, 10Gbps, 3750GB de almacenamiento)
- Cada nodo se compuso de 1 Redis, 9 fetchers y 6 procesos parser
- En Redis se almacenaron el frontier por dominio, la fetch queue, las URL visitadas, Bloom filter, robots.txt, la cola de parsing, etc.
- Fetcher: toma URL de la cola por dominio y las obtiene; con asyncio maneja entre 6000 y 7000 tareas concurrentes, y el principal cuello de botella es la CPU
- Parser: 80 workers async, dedicados al parsing de HTML y extracción de enlaces; trabajo centrado en CPU
- Almacenamiento: en lugar de S3, se eligió almacenamiento local de la instancia para reducir el costo de guardar páginas grandes
- Sharding: distribución por dominio entre nodos (sin cross-communication), ajustando la cantidad de nodos por shard para resolver el desbalance de dominios populares
Principales alternativas y experimentos
- Se probaron varios almacenes como SQLite y PostgreSQL, pero al final Redis mostró mejor rendimiento
- También se intentó escalado vertical (una sola instancia grande), pero aparecieron cuellos de botella por límites del software, así que finalmente se optó por una estructura de escalado horizontal (varios nodos)
- Se eliminó la cross-communication entre nodos y se procesó en paralelo dentro de cada nodo
Principales lecciones durante el crawling
El parsing fue el mayor cuello de botella
- El tamaño promedio de página creció mucho frente al pasado (51KB en 2012); ahora fue de 242KB en promedio y 138KB de mediana
- Al cambiar de lxml a selectolax (basado en Lexbor), la velocidad de parsing mejoró considerablemente
- Se mejoró la eficiencia truncando el tamaño máximo de página a 250KB
- Como resultado, se logró parsear 160 páginas por segundo en un solo parser, y finalmente se ajustó la proporción fetcher:parser a 9:6 para procesar unas 950 páginas por segundo
Fetching: qué se volvió más fácil y qué más difícil
- El ancho de banda de red no fue un cuello de botella (de 25Gbps por nodo, solo se usaron alrededor de 8Gbps)
- El cuello de botella de DNS tampoco fue un problema al enfocarse solo en dominios populares
- En cambio, el handshake SSL apareció como uno de los mayores cuellos de botella, representando el 25% del uso total de CPU
- Dado que la mayoría de las páginas migraron a HTTPS, aumentó el costo en CPU
Ejecución real del crawl y problemas
- En los experimentos iniciales se trabajó solo unas horas con un único nodo (
i7i.2xlarge), y luego el crawl principal se amplió a 12 nodos
- Surgieron problemas de memoria: el frontier (URL no visitadas) de dominios populares creció hasta decenas de GB, provocando caídas repetidas de nodos
- Dominios populares (por ejemplo, yahoo.com, wikipedia.org) o sitios con cantidades anormalmente altas de enlaces causaron problemas
- Los dominios problemáticos se excluyeron manualmente, y tras fallas se recuperó reiniciando nodos y truncando el frontier
Comparación entre teoría y práctica
- En comparación con la estimación más académica de "10 mil millones de páginas en 5 días con 5 máquinas", las cifras reales resultaron relativamente cercanas
- Considerando la utilización real de red y CPU de cada nodo, sería posible alcanzar un mayor throughput dependiendo del nivel de optimización
Tareas futuras y reflexiones
- Se confirmó de nuevo que solo con parsing de HTML ya es posible acceder a una buena cantidad de páginas web; sin embargo, en plataformas grandes (por ejemplo, GitHub) el contenido significativo está dentro de JS y no se puede parsear
- Como tarea futura, hace falta explorar los costos y métodos del crawling masivo basado en renderizado de JS
- También se menciona como tema posterior el análisis de datos (metadatos reales de las páginas recolectadas, proporción activas/inactivas, etc.)
- Recientemente ha aumentado el crawling agresivo combinado con IA, y están surgiendo nuevos sistemas de defensa como pay-per-crawl de Cloudflare, por lo que el entorno del web crawling vuelve a estar cambiando
3 comentarios
Impresionante... aplausos...
Interesante. Lo disfruté mucho, gracias.
Impresionante... ¿es una pelea entre la lanza y el escudo? jaja