Todos los artículos

· Casimiro Ferreira· 4 min de lectura

Robots.txt, sitemaps y scraping web ético

  • Web Scraping
  • Sitemaps
  • Ethics
  • Robots.txt
  • Data Collection
  • FOSS

Empieza por el reconocimiento, no por la fuerza bruta

Los peores scrapers rastrean a ciegas. Machacan un sitio, ignoran las declaraciones de crawl-delay, revuelven cada ruta buscando datos, y se rompen cuando la estructura cambia por un solo nombre de clase. Los mejores scrapers empiezan por leer el sitio.

Todo sitio web publica un contrato en tres lugares: robots.txt (política de rastreo), los sitemaps (lo que el propio sitio considera digno de indexación), y el grafo de enlaces (cómo están realmente conectadas las páginas). Leer esto primero responde a tres preguntas antes de que escribas una sola línea de código de scraping:

  1. ¿Es rastreable este sitio? ¿Qué permite robots.txt, y a qué ritmo?
  2. ¿Dónde están los datos? ¿Qué exponen los sitemaps?
  3. ¿Cómo está estructurado el sitio? ¿Qué aspecto tiene la topología de enlaces?

Eso es lo que hace sitemapper.

Descubrimiento pasivo: robots.txt + sitemaps

discover() obtiene robots.txt y todos los sitemaps que puede encontrar — incluidas las directivas Sitemap:, los índices de sitemaps que apuntan a sub-sitemaps, y los archivos comprimidos con gzip — sin rastrear una sola página HTML:

from sitemapper import discover

info = discover("https://www.python.org")
print(info.summary())
# Base URL:       https://www.python.org
# Blocked:        False
# Sitemaps found: 1
# URLs in sitemaps: 342
# Crawl-delay:    None
# Sitemap directives in robots.txt: 1

# What pace does the site ask for?
if info.robots.crawl_delay:
    print(f"Wait {info.robots.crawl_delay}s between requests")

# May I fetch this path?
info.robots.is_allowed("/api/users")            # True / False
info.robots.is_allowed("/admin", user_agent="MyBot/1.0")

# Every deduplicated URL the site's own sitemaps declare
for url in info.urls:
    print(url.loc, url.lastmod, url.changefreq, url.priority)

El detalle por agente está ahí cuando lo necesitas: info.robots.groups contiene cada bloque User-agent con sus allows, disallows y crawl_delay, en orden de documento. Si un sitio no tiene robots.txt en absoluto, is_allowed() devuelve True para todo — la ausencia de una política es en sí misma la política.

La recompensa del scraping que empieza por el sitemap: en lugar de descubrir URL rastreando (lento, ruidoso, incompleto), partes de la propia lista de los responsables. Rastreas lo que el sitio declara importante, al ritmo que declara aceptable, con una fracción de las peticiones.

Descubrimiento activo: el grafo de enlaces

Algunos sitios no publican ningún sitemap. Para esos, crawl() ejecuta un rastreo acotado en anchura desde la URL base y devuelve un LinkGraph de páginas internas y enlaces salientes:

from sitemapper import crawl

graph = crawl("https://example.com", max_pages=50, max_depth=2)
print(graph.summary())
# Pages crawled (internal): 50
# External URLs seen: 87
# Top external domains: ...

Esto te indica la topología real — qué páginas enlazan con qué — para que puedas decidir si el sitio merece un scraper estructurado siquiera. El descubrimiento y el rastreo son deliberadamente llamadas separadas: el paso pasivo nunca obtiene HTML, así que siempre puedes explorar con educación antes de decidir rastrear.

Construido sobre el mismo transporte resiliente

El reconocimiento del sitio no sirve de nada si el propio reconocimiento acaba tras un muro anti-bot. Todo el HTTP de sitemapper pasa por unblock_requests — el transporte que suplanta el TLS de nuestra entrada sobre transporte anti-bot — para que robots.txt y los sitemaps regresen incluso en sitios protegidos por Cloudflare. Una instancia de FlareSolverr o un respaldo a Wayback Machine se pueden activar con variables de entorno (SITEMAPPER_FLARESOLVERR_URL, SITEMAPPER_WAYBACK_FALLBACK=1) o mediante la clase Sitemapper.

Por qué esto importa

Crawl delay: un sitio que declara Crawl-delay: 2 te está diciendo a qué velocidad quiere que lo golpeen. Ignóralo y te bloquean — o degradas el sitio para todos. Respétalo y tu scraper juega limpio.

Sitemaps antes que rastreo: un sitemap enumera lo que el sitio quiere indexar. El rastreo ciego de enlaces puede tocar cinco veces más URL para encontrar el mismo contenido. Parte del sitemap cuando exista uno; es más rápido para ti y más ligero para el servidor.

Delimita el alcance antes del código: algunos sitios prohíben el scraping de plano en robots.txt; otros tienen sitemaps que ya contienen todo lo que necesitas. Diez segundos de discover() te dicen en qué situación estás antes de que inviertas en un parser.

La herramienta

pip install sitemapper
pip install sitemapper[stealth]   # adds curl_cffi TLS impersonation

Úsala como biblioteca, o desde la línea de comandos — --json FILE escribe el descubrimiento completo en un archivo para que otras herramientas lo consuman, --crawl añade el paso del grafo de enlaces:

python -m sitemapper https://example.com
python -m sitemapper https://example.com --crawl --max-pages 50 --json out.json

Es software libre y funciona en tu propio hardware. Empieza cada scraper con reconocimiento.