Qué es realmente un crawler
Un crawler hace tres cosas: descargar, analizar y decidir. Descargar trae el HTML por HTTP. Analizar extrae datos estructurados de él. Decidir elige si seguir los enlaces, cuándo parar y a qué velocidad ir. Los crawlers modernos añaden una capa de almacenamiento y una cola, pero en el fondo son esas tres cosas.
El playground de arriba ejecuta una versión de las dos primeras aislada en un worker. Pruébalo: pulsa Ejecutar. Verás el título de la página de prueba, sus h1 y los 10 primeros enlaces impresos como JSON.
Tres formas de analizar HTML
| Enfoque | Cuándo usarlo | Pros y contras |
|---|---|---|
| Regex (lo que usa el playground) | Extraer un patrón evidente de un HTML con estructura conocida | Rápido y frágil. Se rompe con marcado mal formado. No analices HTML arbitrario así. |
DOMParser (en el navegador o en un worker con polyfill del DOM) | Cualquier scraping en el cliente, cualquier HTML razonable | Acceso real al DOM; funciona en un Worker con linkedom o un polyfill similar. |
cheerio (Node.js, en el servidor) | API al estilo jQuery; la mayoría de scrapers de servidor | Necesita el runtime de Node; más ligero que jsdom completo. |
jsdom | Necesitas ejecutar scripts | Pesado. Recurre a él solo si tienes que ejecutar el JS de la página. |
El playground usa regex porque la página de prueba tiene una estructura conocida y queremos que la versión aislada en el worker sea pequeña. Un crawler en producción debería usar casi siempre cheerio (Node) o linkedom (navegador/worker).
Buenos modales
Todo crawler de verdad necesita cuatro cosas que el playground no tiene:
1. Respetar robots.txt. Antes de descargar https://example.com/foo, descarga https://example.com/robots.txt y comprueba que tu user agent está permitido. La mitad de las historias de crawlers bloqueados se deben a un robots.txt que podrías haber leído.
2. Una cabecera User-Agent real. Identifícate:
fetch(url, {
headers: {
'User-Agent': 'VorluxBot/1.0 (+https://vorluxai.com/bots)',
},
});
Así los dueños de los sitios pueden bloquearte si quieren. Te interesa que sea así: es el pacto que permite que el crawling siga funcionando para todos.
3. Limitar la frecuencia. Una petición cada 250 ms por host es el “valor por defecto educado” del sector. Si vas más agresivo, te limitarán o te banearán; si vas más despacio, no terminarás en este siglo.
async function sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}
for (const url of urls) {
await fetch(url);
await sleep(250);
}
4. Caché de respuestas. Si vuelves a rastrear a diario, revisa las cabeceras Last-Modified / ETag y envía If-None-Match en la segunda pasada. Te ahorrarás el 90 % de los bytes.
Cuando des el salto a una herramienta de verdad
- Tu propio script en Node/Deno — lo que escribirías si el objetivo tiene menos de 100 páginas y es algo puntual.
- Scrapy (Python) — cuando quieres middlewares integrados, una cola en condiciones, reintentos y más de 10.000 páginas. El estándar de facto.
- Playwright (Node) — cuando las páginas son SPA renderizadas con JS y necesitas un navegador real. Más lento por página, pero inevitable en algunos sitios.
- Apify SDK — cuando quieres crawlers alojados que no tengas que mantener tú. Además, publican su SDK como código abierto para que lo alojes tú mismo.
Nosotros combinamos: Playwright para objetivos con mucha SPA, cheerio + un planificador propio para HTML estático, y Scrapy solo cuando alguien de la casa ya lo domina.
Cómo lo usa VORLUX en producción
Nuestro crawler de la base de conocimiento es un servicio Node basado en cheerio que corre en el orquestador. Descarga fuentes fiables conocidas (documentación de proveedores de LMS, publicaciones del EU AI Act, sitios regulatorios españoles), las analiza respetando su estructura y vuelca el contenido en el almacén SQLite vorlux_content.db con indexación FTS5. Está limitado a una petición cada 400 ms por host. El agregador de noticias es un trabajo aparte con Playwright para un puñado de sitios de noticias basados en SPA que no renderizan en el servidor.
El panel del bucle de rastreo te muestra la profundidad de la cola, la tasa de éxito y la última descarga de cada fuente. Volvemos a ejecutar el crawler de la base de conocimiento cada noche y el agregador de noticias cada 4 horas. Unas 1.400 páginas de la base de conocimiento actual han llegado por este pipeline.
Ve a probarlo
En el playground, cambia la URL de descarga por la de tu propio sitio y modifica la regex para agrupar todas las etiquetas <a> por hostname; deberías obtener algo como {"vorluxai.com": 12, "animejs.com": 3, "codesandbox.io": 1}.
Solución
(async () => {
const res = await fetch('YOUR_URL_HERE');
const html = await res.text();
const links = [...html.matchAll(/<a[^>]+href="([^"]+)"/gi)].map((m) => m[1]);
const byHost = {};
for (const link of links) {
try {
const host = new URL(link, 'YOUR_URL_HERE').hostname;
byHost[host] = (byHost[host] || 0) + 1;
} catch {
/* URL relativa o mal formada: se omite */
}
}
self.postMessage({ type: 'result', data: byHost });
})();Por dónde seguir
- Documentación de Scrapy — Architecture overview — solo el diagrama ya merece la pena.
- Playwright — guía de web scraping — cuando fetch no basta.
- Academia de web scraping de Apify — un buen curso si quieres dedicarte a esto profesionalmente.
Si estás montando un pipeline de ingesta de datos y quieres ayuda de nivel de producción, es una de las cosas en las que Vorlux ofrece consultoría. Escríbenos.