media webcrawlingdata-ingestionjavascript 6 min de lectura

Rastrea una página web

Guía honesta sobre crawlers: descargar y analizar es la mayor parte del trabajo. Te enseñamos el resto, con playground en vivo.

Documentación ↗

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

EnfoqueCuándo usarloPros y contras
Regex (lo que usa el playground)Extraer un patrón evidente de un HTML con estructura conocidaRá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 razonableAcceso 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 servidorNecesita el runtime de Node; más ligero que jsdom completo.
jsdomNecesitas ejecutar scriptsPesado. 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

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

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.