Volver al blog

Crawling Basado en Sitemap: Encuentra URLs Antes de Recuperarlas

James Thompson
James Thompson

Scraping and Proxy Management Expert

22-Jul-2026

TL;DR:

  • La mayoría de los rastreadores descubren URLs siguiendo enlaces, lo que significa obtener páginas que no deseas para encontrar las que sí.
  • Un sitemap te proporciona la lista de antemano: una solicitud HTTP devuelve cada URL que el sitio desea indexar, ya desduplicada por el editor.
  • Filtrar un sitemap real en esta guía redujo 7,577 entradas a las 597 que coincidían con la sección objetivo, antes de que se obtuviera una sola página.
  • Establece un presupuesto de rastreo explícito a partir de esa lista en lugar de rastrear hasta que algo te detenga: el límite debe estar en el código, no en tus intenciones.
  • Solo renderiza JavaScript cuando los datos lo necesiten; las páginas en este recorrido sirven sus títulos del lado del servidor, por lo que una solicitud no renderizada es más rápida y devuelve el mismo campo.
  • Comienza con el plan gratuito de Scrapeless y dirije el paso de descubrimiento a un sitemap que te esté permitido rastrear.

Los rastreadores que siguen enlaces funcionan, pero son ineficientes. Para llegar a las páginas de artículos de un sitio, obtienes la página de inicio, listados de categorías, paginación, páginas de etiquetas y cualquier otra cosa que se encuentre entre el punto de entrada y el contenido, y luego tiras la mayor parte.

Un sitemap omite eso. Es una lista de URLs que el editor desea que se descubran explícitamente, lo que lo convierte en el mecanismo de descubrimiento más barato disponible y el menos probable en molestar a alguien.

Lo que un Sitemap te Ofrece

Un sitemap es un archivo XML que enumera las URLs de un sitio, definido por el protocolo de Sitemaps. Existen dos formas y debes manejar ambas:

  • Un urlset contiene entradas <url><loc> que apuntan a páginas.
  • Un sitemapindex contiene entradas <sitemap><loc> que apuntan a otros sitemaps, utilizado cuando un sitio excede el límite de 50,000 URLs o 50 MB por archivo del protocolo.

Ambos usan el mismo elemento <loc>, por lo que el parser a continuación lee <loc> y luego decide qué está mirando. Los sitemaps también se sirven comúnmente comprimidos en gzip, por lo que un fetcher debería manejar eso de manera transparente.

Dónde encontrar uno: verifica primero /sitemap.xml, luego la directiva Sitemap: dentro de robots.txt, que el estándar del Protocolo de Exclusión de Robots define como el lugar canónico para publicitarlo.

Prerrequisitos

  • Python 3.9 o superior. El paso de descubrimiento utiliza solo la biblioteca estándar.
  • Una clave API de Scrapeless para el paso de obtención:
bash Copy
export SCRAPELESS_API_KEY="tu_clave_api_aqui"

Los ejemplos a continuación apuntan al sitemap de Scrapeless, por lo que el recorrido es seguro de ejecutar tal como está escrito. Cambia por un sitemap que te esté permitido rastrear cuando lo adaptes.

Descubrir y Filtrar

Una solicitud, luego filtrado en memoria. Aún no se ha obtenido nada del sitio:

python Copy
import gzip, re, urllib.request

SITEMAP = "https://www.scrapeless.com/sitemap.xml"
LOC_RE = re.compile(r"<loc>\s*([^<\s]+)\s*</loc>", re.I)

def fetch_xml(url: str) -> str:
    req = urllib.request.Request(url, headers={"User-Agent": "sitemap-demo/1.0"})
    with urllib.request.urlopen(req, timeout=60) as r:
        raw = r.read()
    if url.endswith(".gz") or raw[:2] == b"\x1f\x8b":
        raw = gzip.decompress(raw)
    return raw.decode("utf-8", errors="replace")

xml = fetch_xml(SITEMAP)
is_index = "<sitemapindex" in xml.lower()
urls = LOC_RE.findall(xml)

print(f"tipo de documento: {'índice de sitemap' if is_index else 'conjunto de urls'}")
print(f"total de entradas <loc>: {len(urls)}")

blog = sorted({u for u in urls if "/en/blog/" in u})
print(f"filtrado a /en/blog/: {len(blog)}")
print(f"primero: {blog[0]}")
text Copy
tipo de documento: conjunto de urls
total de entradas <loc>: 7,577
filtrado a /en/blog/: 597
primero: https://www.scrapeless.com/es/blog/10-best-no-code-web-scrapers

Ese es todo el argumento a favor de este enfoque: 7,577 URLs enumeradas y reducidas a 597 relevantes, a un costo de una solicitud. Un rastreador que siga enlaces habría obtenido cientos de páginas para alcanzar la misma lista y aún habría perdido cualquier cosa que no estuviera vinculada desde una ruta que le tocara recorrer.

Tres detalles en ese código son importantes.

La verificación de gzip inspecciona los bytes mágicos en lugar de confiar en la extensión del archivo, porque muchos servidores devuelven contenido comprimido en gzip desde una URL .xml. La firma de dos bytes \x1f\x8b que busca es la cabecera definida en la especificación del formato de archivo GZIP.

sorted({...}) es una comprensión de conjunto, por lo que las URLs duplicadas colapsan antes de contarlas. Los sitemaps contienen duplicados, especialmente cuando un sitio se compone de varios generadores.

Detectar <sitemapindex> es importante porque un índice significa que los valores <loc> que acabas de recolectar son más sitemaps, no páginas. Si is_index es verdadero, obtén cada uno de esos y repite antes de tratar cualquier cosa como una URL de página.

Establecer un Presupuesto Explícito, Luego Obtener

El descubrimiento es barato; la recuperación no lo es. El número de páginas que recuperarás debería ser una constante en el código, no una propiedad emergente de cuánto tiempo se ejecuta el bucle:

python Copy
import json, os, re, urllib.request

SITEMAP = "https://www.scrapeless.com/sitemap.xml"
API = "https://api.scrapeless.com/api/v2/unlocker/request"
LOC_RE = re.compile(r"<loc>\s*([^<\s]+)\s*</loc>", re.I)
TITLE_RE = re.compile(r"<title[^>]*>(.*?)</title>", re.S | re.I)

def fetch_sitemap(url: str) -> str:
    req = urllib.request.Request(url, headers={"User-Agent": "sitemap-demo/1.0"})
    with urllib.request.urlopen(req, timeout=60) as r:
        return r.read().decode("utf-8", errors="replace")

def fetch_page(url: str) -> str:
    body = json.dumps({
        "actor": "unlocker.webunlocker",
        "input": {"url": url, "js_render": False},
    }).encode()
    req = urllib.request.Request(API, data=body, headers={
        "x-api-token": os.environ["SCRAPELESS_API_KEY"],
        "Content-Type": "application/json",
    })
    with urllib.request.urlopen(req, timeout=180) as r:
        return json.loads(r.read())["data"]

urls = sorted({u for u in LOC_RE.findall(fetch_sitemap(SITEMAP)) if "/en/blog/" in u})
print(f"candidatos después de filtrar y deduplicar: {len(urls)}")

BUDGET = 3
print(f"presupuesto para rastrear: {BUDGET}")

for url in urls[:BUDGET]:
    html = fetch_page(url)
    m = TITLE_RE.search(html)
    title = re.sub(r"<[^>]+>", "", m.group(1)).strip() if m else "(sin título)"
    print(f"  {url.rsplit('/', 1)[-1]} -> {title}")
text Copy
candidatos después de filtrar y deduplicar: 597
presupuesto para rastrear: 3
  10-best-no-code-web-scrapers -> 10 Mejores Scrapers Web Sin Código para Extracción de Datos Sin Esfuerzo en 2025
  20-ways-for-web-scraping-without-getting-blocked -> 20 Formas de Hacer Web Scraping Sin Ser Bloqueado
  403-web-scraping -> Error 403 en Web Scraping: 10 Soluciones Fáciles para Arreglar Solicitudes Prohibidas

BUDGET es una constante nombrada aplicada con un corte. Eso es deliberado: un rastreador cuya condición de detención es "la lista se acabó" se comporta de manera muy diferente en una sección de unos pocos cientos de URL que en una con decenas de miles, y la diferencia solo aparece en producción.

Nota que js_render está configurado en False. Estas páginas sirven su <title> en el HTML inicial, por lo que el renderizado añadiría coste y latencia para un campo que ya está ahí. Renderiza cuando los datos que necesitas están escritos en el DOM por un script — no por defecto. La API de Scraping Universal de Scrapeless maneja ambos, y la guía de renderizado JS cubre cómo determinar en qué caso te encuentras.

El conteo de URL es un instantáneo. Un sitemap es un documento en vivo, por lo que volver a realizar el descubrimiento en un día diferente devuelve un número diferente — ese es el punto de leerlo en cada ejecución en lugar de codificar una lista.

Solución de problemas

/sitemap.xml devuelve 404. Lee robots.txt y busca una línea Sitemap:, que puede señalar a otro lugar por completo. Algunos sitios publican uno solo allí; algunos no publican ninguno, en cuyo caso seguir enlaces es tu opción restante.

El analizador devuelve cero URL de un archivo que parece válido. Verifica si la respuesta estaba comprimida con gzip y no descomprimida — la verificación del byte mágico anterior maneja el caso común. También confirma que obtuviste XML y no una página de error HTML, que es lo que produce una redirección a un amigable 404.

Los conteos parecen demasiado bajos. Es probable que el documento sea un índice de sitemap. Cada <loc> en él es otro sitemap para recuperar, y las URL de las páginas están un nivel más abajo.

Las entradas apuntan a URL que ya no existen. Los sitemaps se generan, y los generadores se retrasan. Trata la lista como candidatos en lugar de garantías, y espera que algunas entradas devuelvan 404.

Revisa los términos del sitio y su robots.txt antes de rastrear cualquier cosa, mantén la colección a páginas públicas, y manten el presupuesto proporcional a lo que el sitio puede ofrecer cómodamente. Un sitemap te dice lo que un editor está dispuesto a indexar; no es una licencia para recuperar todo tan rápido como puedas.

Conclusión

El rastreo impulsado por sitemaps reemplaza la parte más derrochadora de un rastreador — el descubrimiento — con una única solicitud. En esta guía, eso significó enumerar 7,577 URL, reduciendo a las 597 que importaban, y recuperando solo 3, con el límite escrito en el código en lugar de dejarlo al azar.

El hábito que se generaliza más allá de los sitemaps es el orden: enumera primero, filtra y deduplica mientras aún es gratis, decide explícitamente cuánto vas a recuperar, y solo entonces comienza a hacer solicitudes. La mayoría de los rastreadores que se meten en problemas hacen esos pasos en el orden opuesto.
Comienza con el plan gratuito de Scrapeless para ejecutar el paso de recolección contra una fuente que tengas permiso de rastrear y revisa los precios de Scrapeless cuando dimensionas un trabajo recurrente.

Preguntas Frecuentes

P: ¿Cómo encuentro el sitemap de un sitio web?

Intenta primero con /sitemap.xml, luego lee robots.txt para encontrar una directiva Sitemap:, que el RFC 9309 define como el lugar canónico para publicitar uno. Los sitios grandes a menudo publican un índice en esa ruta que apunta a varios sitemaps de secciones en lugar de un solo archivo plano.

P: ¿Cuál es la diferencia entre un índice de sitemap y un conjunto de URLs?

Un conjunto de URLs lista las URLs de las páginas; un índice de sitemap lista otros archivos de sitemap. Ambos utilizan elementos <loc>, por lo que un analizador que solo mira <loc> devolverá felizmente las URLs de sitemap mientras crees que tienes páginas. Busca un elemento raíz <sitemapindex> y recursiona cuando encuentres uno; la división existe porque el protocolo limita un solo archivo a 50,000 URLs o 50 MB.

P: ¿Es mejor rastrear desde un sitemap que seguir enlaces?

Para descubrimiento, generalmente sí: una solicitud devuelve URLs que el editor ha listado explícitamente, en lugar de obtener páginas intermedias para encontrarlas. La compensación es la cobertura: un sitemap solo contiene lo que el generador eligió incluir, por lo que las páginas que existen pero fueron omitidas permanecen invisibles. Seguir enlaces aún gana cuando necesitas todo lo accesible en lugar de todo lo publicitado.

P: ¿Debo renderizar JavaScript al rastrear desde un sitemap?

Solo cuando el campo que deseas no esté en el HTML inicial. Las páginas en esta guía sirven su <title> del lado del servidor, por lo que js_render se establece en False y la recolección es más barata y rápida. Verifica primero una página: si los datos aparecen en el código fuente, no necesitas renderización.

P: ¿Cuántas páginas debo recopilar por ejecución?

Establece un número explícitamente y corta la lista de candidatos a eso, como hace BUDGET arriba. El valor correcto depende de la capacidad del sitio y tus necesidades, pero la parte importante es que es una decisión registrada en el código en lugar de un efecto secundario de la longitud de la lista, lo que convierte un trabajo de sección pequeño y un trabajo de todo el sitio en eventos muy diferentes.

En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.

Artículos más populares

Catalogar