Geonode logo
Geonode Team

Geonode Team

Actualizado: 7 de octubre de 2026

Publicado: 2 de septiembre de 2026

Cómo crear una lista de páginas para rastrear: una guía completa

Una lista de rastreo es el conjunto de URL que tu rastreador tiene previsto visitar, y gestionarla bien es lo que marca la diferencia entre un rastreador que termina su tarea y uno que se ejecuta indefinidamente. Los problemas interesantes no residen en la obtención de datos, sino en decidir qué obtener a continuación, reconocer que dos URL corresponden a la misma página y saber cuándo detenerse. Esta guía aborda la inicialización, la normalización, la frontera, la priorización y los presupuestos, con los detalles de especificación que hacen que la normalización sea correcta en lugar de aproximada.

Nuestra postura: somos Geonode y vendemos proxies, que son un recurso para el rastreo, pero no para la gestión de listas. La verdad es que una lista de rastreo mal gestionada sale mucho más cara que un plan de proxies mal elegido. Un rastreador sin normalización de URL visita la misma página docenas de veces con diferentes órdenes de parámetros de consulta, y con la tarificación por gigabyte acabas pagando por cada una de ellas. Un rastreador sin presupuestos sigue un calendario hasta el infinito y te lo factura. Corregir la lista es gratis; corregirla después de recibir la factura, no. Esa sección se encuentra más abajo, y es la que merece la pena leer primero.

¿Qué es realmente una lista de rastreo?

Tres conceptos que a menudo se confunden.

La lista inicial: el punto de partida. Un puñado de URL o un mapa del sitio completo.

La frontera: las URL descubiertas y puestas en cola, pero que aún no se han recuperado. Esta es la estructura de datos activa y la que recoge todas las decisiones de diseño.

El conjunto de páginas visitadas: las URL ya recuperadas, que se conservan para no volver a recuperarlas.

El ciclo de vida es un bucle: se toma una URL de la frontera, se recurre, se extraen los enlaces, se normalizan, se descartan los que ya están en el conjunto de visitas o en la cola, se añaden el resto a la frontera y se marca la URL como visitada. Se repite hasta que la frontera quede vacía o se agote el presupuesto.

Es sencillo a grandes rasgos. Cada paso tiene un detalle que te costará un día si lo haces mal.

Semilla: de dónde proceden las primeras URL

Por orden de la cantidad de trabajo que ahorra cada una.

El mapa del sitio. La mejor semilla disponible, ya que es el propio inventario del sitio e incluye marcas de tiempo lastmod que indican qué ha cambiado. Encuéntralo mediante la directiva Sitemap: en robots.txt y sigue los archivos de índice del mapa del sitio de forma recursiva.

Un feed o API de un socio. Si existe uno, es posible que ni siquiera necesites realizar un rastreo.

Archivos web. La API CDX de Internet Archive devuelve URL históricas de un dominio, incluidas páginas a las que ya no enlaza ningún sitio. No le cuesta nada al sitio de destino y encuentra páginas huérfanas que el rastreo no puede detectar.

Operadores de búsqueda. Las consultas del tipo «site:» muestran páginas indexadas y, lo que es aún más útil, subdominios que desconocías.

Páginas de categorías e índices. Para un rastreo específico, partir de las páginas de listado concretas que te interesan es mucho más eficiente que empezar por la página de inicio y dejar que las cosas sigan su curso.

La página de inicio. El último recurso, y lo primero a lo que recurre la gente por defecto. Empezar por una URL y descubrirlo todo mediante el recorrido de enlaces es la ruta más lenta hacia la peor cobertura.

Hemos repasado toda la jerarquía de fuentes en cómo encontrar todas las páginas de un sitio web. La versión resumida: una buena lista de semillas convierte un rastreo en una simple recuperación.

Normalización de URL: el paso que todo el mundo se salta

El elemento de ingeniería más valioso de un rastreador y el que más se suele omitir.

Sin ella, estas son cinco URL diferentes para tu conjunto de páginas visitadas y una sola página para el servidor:

http://Example.com/Products
http://example.com/products
http://example.com/products/
http://example.com:80/products
http://example.com/products?utm_source=email

RFC 3986 define las normalizaciones que siempre son seguras.

Normalización de mayúsculas y minúsculas. «El esquema y el host no distinguen entre mayúsculas y minúsculas y, por lo tanto, deben normalizarse a minúsculas. Por ejemplo, la URI <HTTP://www.EXAMPLE.com/>

es equivalente a <http://www.example.com/>."

. Ten en cuenta la salvedad: «Se supone que los demás componentes sintácticos genéricos distinguen entre mayúsculas y minúsculas, a menos que el esquema defina específicamente lo contrario». Las rutas distinguen entre mayúsculas y minúsculas; no las conviertas a minúsculas.

Además: «los dígitos hexadecimales dentro de un triplete de codificación porcentual (p. ej., %3a

frente a %3A

) no distinguen entre mayúsculas y minúsculas y, por lo tanto, deben normalizarse para utilizar letras mayúsculas».

Normalización de la codificación porcentual. El RFC se refiere a esto como «una fuente frecuente de variación entre URI que, por lo demás, son idénticas», ya que «algunos creadores de URI codifican con el signo de porcentaje octetos que no requieren dicha codificación». Estos «deben normalizarse descodificando cualquier octeto codificado con el signo de porcentaje que corresponda a un carácter no reservado».

Normalización de segmentos de ruta. Se deben eliminar los segmentos .

y ..

aplicando el algoritmo remove_dot_segments

, ya que «algunas implementaciones en producción asumen erróneamente que la resolución de la referencia no es necesaria cuando la referencia ya es un URI».

Normalización basada en el esquema. El RFC ofrece el ejemplo canónico: estos cuatro son equivalentes:

http://example.com
http://example.com/
http://example.com:/
http://example.com:80/

Por lo tanto, una ruta vacía «debería normalizarse a una ruta de /

», y un puerto predeterminado o vacío «debería eliminarse mediante la normalización basada en el esquema».

Más allá de la especificación, hay tres normalizaciones que son pragmáticas más que estrictamente seguras, y que conviene aplicar con cuidado:

Eliminar los parámetros de seguimiento. utm_*

, fbclid

, gclid

, identificadores de sesión. Estos casi nunca modifican el contenido y multiplican enormemente el número de URL. Mantén una lista en lugar de ir a ciegas.

Ordena los parámetros de consulta restantes. ?a=1&b=2

y ?b=2&a=1

suelen corresponder a la misma página. Por lo general — algunas aplicaciones son sensibles al orden, así que pruébalo con una muestra.

Elimina los fragmentos. #section

es un fragmento del lado del cliente y nunca llega al servidor. Siempre es seguro omitirlo a efectos de rastreo.

**Y respeta rel="canonical"

.** Si una página declara una URL canónica, eso significa que el sitio te está indicando cuál de varias direcciones es la verdadera. Respetarla supone una deduplicación gratuita respaldada por la propia autoridad del sitio.

La frontera: diseño de colas

Hay tres propiedades que determinan si tu rastreador es escalable.

La deduplicación debe ser económica. Antes de añadir una URL, compruebas si ya se conoce. Con un millón de URL, un escaneo lineal resulta inviable. Un conjunto hash funciona hasta cierto punto; más allá de eso, un filtro de Bloom te ofrece una comprobación de pertenencia en tiempo constante con una fracción de la memoria, y con una tasa de falsos positivos reducida —lo que significa que, ocasionalmente, te saltas una URL que no has visto—. Para la mayoría de los rastreos, esa compensación es aceptable; cuando la exhaustividad sea importante, complementa el filtro con un almacén exacto.

El orden debe ser controlable. Una cola FIFO simple proporciona un recorrido en anchura, que suele ser lo que se busca: alcanza una amplia cobertura desde el principio y se mantiene superficial. Una pila LIFO ofrece un recorrido en profundidad, que se adentra mucho en una rama y rara vez resulta útil para el rastreo de un sitio web. Una cola de prioridades te permite ordenar según lo que desees, como se explica a continuación.

Se debe realizar un seguimiento del estado por host. En la práctica, la frontera no es una sola cola, sino una cola por host, de modo que los límites de cortesía se aplican a cada una de forma independiente. Una única cola global con un límite de tasa global implica que un sitio grande deja sin recursos a todos los demás.

La estructura que escala es un conjunto de colas por host más un programador que selecciona el siguiente host del que se puede obtener información —siendo «elegible» aquel en el que ha transcurrido suficiente tiempo desde la última solicitud dirigida a él.

Priorización

Qué URL recuperar a continuación, cuando no es posible recuperarlas todas.

Por profundidad. Las páginas más cercanas a la raíz suelen ser más importantes. Una opción predeterminada sencilla y eficaz.

Por patrón de ruta. Si quieres páginas de productos, da prioridad a las URL que coincidan con /product/. Esta es la heurística de mayor rendimiento para rastreos específicos y su coste es insignificante.

Por lastmod. A partir del mapa del sitio. Recoge lo que haya cambiado.

Por historial de cambios. Para rastreos recurrentes, las páginas que han cambiado a menudo anteriormente volverán a cambiar con frecuencia.

Por número de enlaces entrantes. Las páginas enlazadas desde muchos sitios suelen ser más relevantes. Su cálculo resulta costoso durante un rastreo, pero merece la pena para trabajos de gran envergadura.

Por valor estimado. Sea cual sea tu objetivo real. Si buscas precios, da prioridad a las páginas que probablemente los contengan.

La solución práctica consiste en una pequeña puntuación entera calculada en el momento de la incorporación a la cola a partir de un par de estas señales, que se utiliza como clave en una cola de prioridades. Los esquemas elaborados rara vez justifican su complejidad; la profundidad, más una bonificación por patrón de ruta, cubre la mayoría de las necesidades.

Límites: presupuestos y trampas

Sin límites, algunos rastreos no terminan nunca. Esto no es un caso excepcional.

Profundidad máxima. Enlaces de enlaces de enlaces. Un límite de profundidad de cinco o seis cubre prácticamente cualquier estructura real de un sitio web.

Número máximo de páginas por servidor. Una cifra fija. Cuando se alcance, hay que detenerse e informar en lugar de continuar.

Número máximo total de páginas. Para todo el trabajo.

Ancho de banda máximo. Especialmente en el tráfico de proxy medido, donde un rastreo sin límites supone una factura sin límites.

Exclusiones por patrón. Los calendarios son el clásico espacio infinito: un enlace next month

genera URL sin fin. La navegación por facetas en un catálogo extenso produce explosiones combinatorias. Excluye estos casos por patrón:

/calendar/
/?filter=
/*?sort=

Detección de contenido duplicado. Calcula el hash del cuerpo. Si cien URL devuelven contenido idéntico, has encontrado un espacio generado en lugar de cien páginas.

Y vigila la tasa de descubrimiento. La alerta más útil: realiza un seguimiento de las URL recién descubiertas por cada página recuperada. En un sitio web finito, esta cifra desciende de forma constante hasta cero. Si se mantiene estable o aumenta, algo está generando URL más rápido de lo que puedes consumirlas: una trampa, un calendario o una explosión de facetas. Ya hemos tratado las versiones deliberadas en las trampas honeypot.

Programación del nuevo rastreo

Para cualquier elemento que se actualice más de una vez, la lista se convierte en una programación.

Clasifica por volatilidad. Las páginas que cambian cada hora necesitan comprobaciones cada hora; las que cambian una vez al año, no. Rastrear todo con la frecuencia que requiere el elemento más volátil es la forma más habitual de sobrepasar el presupuesto.

Utiliza solicitudes condicionales. If-Modified-Since y If-None-Match convierten un nuevo rastreo en una serie de respuestas 304 Not Modified que cuestan unos pocos cientos de bytes cada una. En un nuevo rastreo en el que la mayoría de las páginas no han cambiado, esto reduce tanto tu factura como la carga del servidor en un orden de magnitud.

Adáptate a partir de la observación. Si una página no ha cambiado en diez comprobaciones, compruébala con menos frecuencia. Si ha cambiado en las tres últimas, compruébala más a menudo. Basta con un sencillo retroceso multiplicativo.

Detecta explícitamente las eliminaciones. Las páginas desaparecen y los sitios web rara vez lo anuncian. O bien presta atención a los errores 404 o aplica una política: una URL que no se haya visto en tres rastreos consecutivos se marca como inactiva. Sin esto, tu conjunto de datos se llenará de entradas que ya no existen, lo que merma la confianza más rápidamente que las entradas que faltan.

Almacenamiento y escalabilidad

Una nota sobre cuándo deja de funcionar el enfoque en memoria.

Hasta unas cien mil URL, los conjuntos y las listas de Python funcionan bien. No hay que complicar demasiado las cosas.

Hasta unos pocos millones, una base de datos local —SQLite funciona bien— con índices sobre el hash de la URL y el estado de la recuperación. La persistencia también implica que un rastreo interrumpido se reanude en lugar de reiniciarse, lo cual es más importante que el rendimiento.

Más allá de esa cifra, se necesita una cola adecuada y un almacén de clave-valor, con la frontera particionada por host, de modo que se puedan asignar hosts completos a los trabajadores y la «cortesía» se mantenga correcta sin necesidad de coordinación.

Dos decisiones de diseño que dan sus frutos a cualquier escala:

Almacena el hash de la URL, no solo la URL. Las comparaciones y los índices sobre un hash de longitud fija son más eficientes y el almacenamiento es menor.

Almacenar la respuesta sin procesar, no solo el resultado analizado. Cuando un analizador falla —y fallará—, volver a analizar lo que tienes no cuesta nada, mientras que volver a recuperar la información consume ancho de banda y buena voluntad.

Una implementación mínima

Todo el código en unas cuarenta líneas, para hacer más concretas las partes dinámicas.

import time
from collections import deque, defaultdict
from urllib.parse import urlsplit, urlunsplit, parse_qsl, urlencode

TRACKING = {"utm_source", "utm_medium", "utm_campaign", "fbclid", "gclid"}

def normalise(url):
    p = urlsplit(url)
    host = p.hostname or ""
    port = "" if p.port in (None, 80, 443) else f":{p.port}"
    query = urlencode(sorted(
        (k, v) for k, v in parse_qsl(p.query, keep_blank_values=True)
        if k.lower() not in TRACKING
    ))
    return urlunsplit((p.scheme.lower(), host + port, p.path or "/", query, ""))

class Frontier:
    def __init__(self, delay=1.5, max_per_host=5000):
        self.queues = defaultdict(deque)
        self.seen = set()
        self.next_ok = defaultdict(float)
        self.counts = defaultdict(int)
        self.delay, self.max_per_host = delay, max_per_host

    def add(self, url, depth=0):
        url = normalise(url)
        if url in self.seen or depth > 5:
            return False
        host = urlsplit(url).hostname
        if self.counts[host] >= self.max_per_host:
            return False
        self.seen.add(url)
        self.counts[host] += 1
        self.queues[host].append((url, depth))
        return True

    def next(self):
        now = time.monotonic()
        for host, q in self.queues.items():
            if q and self.next_ok[host] <= now:
                self.next_ok[host] = now + self.delay
                return q.popleft()
        return None

En él se pueden apreciar cinco decisiones de diseño, y cada una de ellas se corresponde con una sección anterior.

La normalización se produce en add(), no en el momento de la recuperación. La deduplicación solo es correcta si la forma canónica es la que entra en el conjunto de páginas visitadas, por lo que normalizar más tarde significa que ya se han almacenado duplicados.

El conjunto de páginas visitadas se rellena al colocarlo en la cola, no al finalizar. De lo contrario, una URL descubierta en veinte páginas se pondría en cola veinte veces antes de que finalizara la primera recuperación.

Las colas son por host y la «cortesía» es por host. next_ok registra cuándo se puede contactar de nuevo con cada host, de modo que un sitio grande no pueda agotar los recursos de los demás y el retraso se aplique donde debe.

Los límites se aplican en el punto de entrada. Los límites de profundidad y por host rechazan las URL antes de que consuman memoria, lo que marca la diferencia entre un rastreo limitado y uno que descubre su límite al agotar la RAM.

next() devuelve None en lugar de bloquear. Esto deja al solicitante libre para decidir si espera, realiza otras tareas o finaliza la operación; un programador que permanece inactivo dentro de la estructura de datos es uno que no se puede instrumentar.

Lo que esto carece deliberadamente es de persistencia, y eso es lo primero que hay que añadir para cualquier aplicación real. Un rastreo que se ha bloqueado y debe reiniciarse desde la lista de semillas ha perdido más que tiempo; ha vuelto a recuperar todo, lo que cuesta ancho de banda y buena voluntad.

Instrumentación de la lista

Qué medir, ya que un rastreo que solo informa del número de «páginas recuperadas» no aporta prácticamente nada.

Tamaño de la frontera a lo largo del tiempo. Debería tender a cero. Un aumento indica un descubrimiento ilimitado.

Ratio de descubrimiento. Nuevas URL por página recuperada, como se ha indicado anteriormente.

Resultados de la obtención por estado, por servidor. Las cifras agregadas ocultan que un servidor falla por completo.

Tasa de duplicados. Cuántas de las URL descubiertas ya se conocían. Una tasa elevada tras la normalización significa que a tu normalización le falta algo.

Bytes por página útil. La cifra que vincula el rastreo con la factura, y la que revela que un navegador sin interfaz gráfica está recuperando megabytes de imágenes que no necesitabas.

Verificaciones de contenido. Si las páginas contienen los marcadores que esperas. Un rastreador que informa de un 100 % de éxito mientras devuelve páginas bloqueadas de forma implícita es un fallo costoso, y solo las verificaciones de contenido lo detectan.

Preguntas relacionadas

¿Qué es una lista de rastreo?

El conjunto de URL que un rastreador pretende visitar, que suele estar compuesto por una lista inicial, un conjunto de URL descubiertas pero aún no recuperadas y un conjunto de URL ya visitadas. La gestión de este conjunto —orden, deduplicación y presupuestos— es lo que más influye a la hora de determinar si un rastreo llega a completarse.

¿Cómo se normalizan las URL para el rastreo?

Pon en minúsculas el esquema y el host, pero no la ruta; escribe en mayúsculas los dígitos hexadecimales codificados con el símbolo de porcentaje; descodifica la codificación de porcentaje innecesaria; elimina los segmentos de puntos; omite los puertos predeterminados; normaliza una ruta vacía a /; elimina los fragmentos; y elimina los parámetros de seguimiento conocidos. La RFC 3986 define todos estos pasos excepto los dos últimos.

¿Qué es una «frontera de rastreo»?

La cola de URL descubiertas que aún no se han recuperado. En la práctica, se trata de un conjunto de colas por host más un programador que selecciona el siguiente host elegible dentro de los límites de cortesía, ya que una única cola global permite que un sitio grande acabe agotando los recursos de todos los demás.

¿Cómo evito que mi rastreador funcione indefinidamente?

Límites estrictos: profundidad máxima, número máximo de páginas por host y en total, y un límite de ancho de banda. Añade exclusiones de patrones para calendarios y navegación por facetas, y activa una alerta cuando la proporción entre las URL recién descubiertas y las páginas recuperadas deje de disminuir.

¿Cómo evito rastrear la misma página dos veces?

Normaliza las URL antes de deduplicarlas, ya que una misma página puede tener muchas direcciones válidas. A continuación, comprueba si forma parte de un conjunto de páginas visitadas —un conjunto hash a pequeña escala, un filtro de Bloom o una base de datos a gran escala—. Respeta el atributo «rel="canonical"» cuando las páginas lo declaren.

¿Con qué frecuencia debo volver a rastrear?

Tan poco como tus requisitos lo permitan, clasificando según la frecuencia con la que cada página cambie realmente. Utiliza lastmod a partir del mapa del sitio y las solicitudes condicionales, de modo que las páginas que no hayan cambiado supongan un 304 en lugar de una recuperación completa. Rastrear todo con la frecuencia de las páginas volátiles es la fuente más común de desperdicio.

¿Qué es un filtro de Bloom y lo necesito?

Un conjunto probabilístico que determina la pertenencia a un conjunto en tiempo constante utilizando muy poca memoria, con una pequeña probabilidad de falsos positivos —lo que significa que, ocasionalmente, se omite una URL que en realidad no se ha visto—. Merece la pena a partir de unos pocos millones de URL; por debajo de esa cifra es innecesario, ya que un conjunto simple es más sencillo y exacto.

¿Cómo detecto que se han eliminado páginas?

Estate atento a los errores 404 y aplica una política para las páginas que simplemente dejan de aparecer; por ejemplo, marca una URL como inactiva si no se ha visto en tres rastreos consecutivos. Sin esto, el conjunto de datos acumula entradas que ya no existen, lo que daña la confianza más rápidamente que las lagunas.

Conclusión

El rastreo consiste principalmente en llevar un registro. La recuperación de datos es un problema ya resuelto gracias a las bibliotecas; lo que requiere ingeniería es decidir qué datos recuperar, reconocer que ya se han recuperado y saber cuándo detenerse.

Hay tres aspectos que merecen una atención desproporcionada en relación con su dificultad. La normalización, porque sin ella visitas la misma página bajo una docena de direcciones y pagas por cada una de ellas —y el RFC 3986 te indica exactamente qué transformaciones son seguras—. Los presupuestos, porque algunos espacios de URL son realmente infinitos y un rastreador sin límites estrictos acabará encontrando uno. Y la tasa de descubrimiento, porque es una única cifra que revela una trampa, un calendario o una explosión de facetas mucho antes de que lo haga la factura.

A continuación, selecciona bien los puntos de partida. Un mapa del sitio convierte un rastreo en una recuperación de lo que ha cambiado; una consulta en el archivo saca a la luz páginas a las que el recorrido de enlaces nunca podría llegar; y ambas cosas no le cuestan nada al sitio de destino. Empezar por la página de inicio y confiar en la suerte es la ruta más lenta hacia el resultado menos completo, y sigue siendo la opción por defecto.