Extraer las URL de un sitemap a CSV o JSON: 5 métodos

Extraer las URL de un sitemap a CSV o JSON: 5 métodos

Para extraer URL de un sitemap, solo necesitas el texto de sus elementos loc. La dificultad está en el espacio de nombres XML: una consulta //loc sin más no encuentra nada. Aquí tienes cinco formas de sacar esa lista en CSV, JSON o texto plano, desde una herramienta en el navegador que no requiere configuración hasta un script de Python que también sigue un índice de sitemaps.

¿Por qué //loc no devuelve nada en un sitemap?

Todo sitemap declara un espacio de nombres en su elemento raíz: <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">. El protocolo de sitemaps.org lo exige. Como es un espacio de nombres predeterminado, sin prefijo, todos los elementos <url> y <loc> del archivo pertenecen a él.

XPath 1.0 trata un nombre sin prefijo como un nombre sin espacio de nombres. Así, //loc busca un elemento <loc> que no tenga espacio de nombres, y un sitemap válido no tiene ninguno. La consulta es XPath correcto. Simplemente no coincide con ningún nodo, y por eso tantos scripts y fórmulas devuelven un resultado vacío.

Hay dos soluciones, y cada método de este artículo usa una de ellas:

  • Buscar el elemento solo por su nombre local: //*[local-name()='loc']. Funciona en cualquier herramienta XPath 1.0, sin configuración.
  • Asociar un prefijo al URI del espacio de nombres y usarlo: //sm:loc, donde sm representa http://www.sitemaps.org/schemas/sitemap/0.9. Python y la mayoría de las bibliotecas lo permiten.

Puedes probar las dos con tu propio archivo en nuestro evaluador XPath online. Da al espacio de nombres predeterminado el prefijo d, así que //d:loc lista cada URL con su línea.

Otra trampa: el índice de sitemaps. Su raíz es <sitemapindex>, y sus elementos <loc> apuntan a otros sitemaps, no a páginas. Google admite hasta 50.000 por índice, todos del mismo sitio salvo que se configure el envío entre sitios. WordPress sirve uno por defecto, como muestra nuestra guía sobre las direcciones del sitemap de WordPress. Cada método de abajo indica cómo trata un índice.

Del sitemap a CSV en el navegador, sin instalar nada

Nuestro comprobador de sitemaps gratuito lee un sitemap que pegas, abres o sueltas en la página, en .xml o .xml.gz. El archivo no sale de tu navegador. Tras la comprobación, la pestaña «URL» muestra una tabla con cuatro columnas: URL, última modificación, frecuencia de cambio y prioridad.

  1. Abre el sitemap en el navegador y guárdalo, o copia su código fuente.
  2. Pégalo en el comprobador de sitemaps, o usa «Abrir archivo…», y pulsa «Comprobar sitemap».
  3. Abre la pestaña «URL». Escribe parte de una dirección en «Filtrar URL» para conservar solo algunas filas, y haz clic en el encabezado de una columna para ordenarla.
  4. Pulsa «Exportar CSV».

El CSV contiene las filas tal como las muestra la tabla, con el filtro y el orden aplicados, y las columnas loc, lastmod, changefreq y priority. Está en UTF-8 con marca de orden de bytes (BOM), así que Excel muestra bien los caracteres acentuados. Una celda que empieza por =, +, - o @ lleva un apóstrofo delante, para que la hoja de cálculo no la ejecute como fórmula. El archivo toma el nombre del archivo que abriste, o sitemap.csv si pegaste el texto.

Hay dos límites. La herramienta solo exporta CSV, no JSON. Y nunca se conecta a otros sitios web, así que no puede leer un sitemap desde su URL ni seguir los sitemaps que lista un índice. En un índice, la pestaña se llama «Sitemaps» y el CSV tiene dos columnas, loc y lastmod: los sitemaps secundarios, no las páginas.

Para obtener JSON a partir de un archivo, el conversor XML a JSON convierte el sitemap entero. Obtienes su estructura, con cada <url> como un objeto, en lugar de una lista plana de filas.

Exportar un sitemap publicado desde Chrome a CSV o JSON

La extensión XMLNest para Chrome, disponible en Chrome Web Store, abre en Chrome la URL de un sitemap publicado y lo muestra como tabla en su vista «Lectura». Encima de la tabla hay tres botones:

  • Exportar CSV guarda las columnas URL, Last modified, Change frequency y Priority, más los recuentos Images y Alternates cuando el sitemap tiene entradas de imágenes o hreflang.
  • Exportar JSON guarda un array de objetos con las claves loc, lastmod, changefreq, priority, images y alternates.
  • Copiar URLs copia solo las direcciones, una por línea, listas para pegarlas en una columna o en un rastreador.

Los tres usan las filas visibles, en el orden en que aparecen. «Filtrar URLs» conserva las direcciones que contienen un texto, y «Cambiadas desde» conserva las URL modificadas en una fecha o después. Si filtras /blog/ y eliges una fecha, exportas solo los artículos recientes. Como en el comprobador de sitemaps, las celdas del CSV que empiezan por =, +, - o @ se escapan.

En un índice, «Cargar todos los sitemaps secundarios» lee los sitemaps del mismo sitio que aparecen en él, 50 como máximo, uno a uno. Nunca pide sitemaps de otros sitios. Después, «Mostrar todas las URLs» los reúne en una sola tabla con una columna «Sitemap» adicional, que indica de qué archivo procede cada URL, y las tres exportaciones también funcionan con esa tabla.

Extraer las URL de un sitemap con curl y xmllint

xmllint forma parte de libxml2. macOS lo incluye, Debian y Ubuntu lo ofrecen en el paquete libxml2-utils, y en Windows puedes usarlo a través de WSL. Su opción --xpath ejecuta una expresión e imprime el resultado:

curl -s https://www.example.com/sitemap.xml \
  | xmllint --xpath "//*[local-name()='loc']/text()" - > urls.txt

El - indica a xmllint que lea de la tubería. Si usas //loc, el manual dice que xmllint imprime «XPath set is empty» y termina con el código 11: es el problema del espacio de nombres descrito arriba. El manual no documenta ninguna opción para asociar un prefijo de espacio de nombres con --xpath, y por eso aquí la forma práctica es la de local-name().

El manual solo dice que cada nodo del resultado se serializa completo. Comprueba que tu versión imprime una URL por línea antes de pasar urls.txt a otro script.

Un sitemap comprimido con gzip necesita un paso más, gunzip, entre la descarga y xmllint:

curl -s https://www.example.com/sitemap.xml.gz | gunzip \
  | xmllint --xpath "//*[local-name()='loc']/text()" -

En un índice, el mismo comando devuelve las direcciones de los sitemaps secundarios. Ejecútalo de nuevo en cada uno de ellos para obtener las páginas:

curl -s https://www.example.com/sitemap_index.xml \
  | xmllint --xpath "//*[local-name()='loc']/text()" - \
  | while read -r sm; do
      curl -s "$sm" | xmllint --xpath "//*[local-name()='loc']/text()" -
    done > all-urls.txt

Ejecutamos el ejemplo de Python de abajo; los comandos de xmllint siguen su manual.

Python: de sitemap a JSON o CSV, con índices incluidos

ElementTree, en Python, expande el espacio de nombres predeterminado dentro del nombre de la etiqueta, así que la raíz de un sitemap es {http://www.sitemaps.org/schemas/sitemap/0.9}urlset. En nuestra prueba con Python 3.14, root.findall(".//loc") devolvió cero elementos. La documentación de Python muestra la solución: pasar un diccionario de prefijos a find() y findall().

Este script lee un sitemap o un índice desde su URL, sigue los sitemaps secundarios de un índice, descomprime los archivos gzip y escribe loc, lastmod y el sitemap de origen en urls.csv:

import csv
import gzip
import sys
import urllib.request
import xml.etree.ElementTree as ET

NS = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}

def read(url):
    with urllib.request.urlopen(url) as response:
        data = response.read()
    if data[:2] == b"\x1f\x8b":  # gzip
        data = gzip.decompress(data)
    return ET.fromstring(data)

def collect(url, rows):
    root = read(url)
    if root.tag == "{%s}sitemapindex" % NS["sm"]:
        for child in root.findall("sm:sitemap/sm:loc", NS):
            collect(child.text.strip(), rows)
    else:
        for entry in root.findall("sm:url", NS):
            rows.append([entry.findtext("sm:loc", "", NS).strip(),
                         entry.findtext("sm:lastmod", "", NS).strip(),
                         url])

rows = []
collect(sys.argv[1], rows)
with open("urls.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["loc", "lastmod", "sitemap"])
    writer.writerows(rows)
print(len(rows), "URLs written to urls.csv")

Ejecútalo con python sitemap_to_csv.py https://www.example.com/sitemap_index.xml. En nuestro índice de prueba, que listaba un sitemap normal y otro .xml.gz, escribió cuatro filas. Una URL escrita ?page=2&amp;sort=new en el XML salió como ?page=2&sort=new, porque el analizador decodifica las entidades.

Para obtener JSON, sustituye el último bloque por json.dump(), de la biblioteca estándar, por ejemplo con una lista de objetos con las claves loc, lastmod y sitemap. Si analizas sitemaps de sitios que no controlas, lee antes la sección sobre seguridad XML de la documentación de Python.

Google Sheets e IMPORTXML: una lista de URL en una columna

La página de ayuda de Google sobre IMPORTXML incluye XML entre los tipos de datos que lee la función. Su sintaxis es IMPORTXML(url, xpath_query, locale), y la URL puede ser una referencia a una celda. Escribe en una celda la forma con local-name():

=IMPORTXML("https://www.example.com/sitemap.xml", "//*[local-name()='loc']")

La página de ayuda no dice nada sobre los espacios de nombres XML. La expresión local-name() no depende de cómo los trate Sheets, porque busca el elemento solo por su nombre. Después, Archivo > Descargar > Valores separados por comas (.csv) guarda la hoja como archivo CSV. No probamos IMPORTXML para este artículo; esta sección sigue la página de ayuda de Google.

En un índice, la fórmula devuelve los sitemaps secundarios. Pon la dirección de cada uno en una celda de la columna A y usa =IMPORTXML(A2, "//*[local-name()='loc']") en la columna siguiente para cada uno de ellos.

Si el propio sitemap no se carga, empieza por nuestro artículo sobre los sitemaps que Google no obtiene: una página de error HTML no devuelve ningún elemento <loc>.

Fuentes

  1. sitemaps.org: Protocolo (consultada el 29 de octubre de 2026)
  2. Google Search Central: Gestionar sitemaps con archivos de índice de sitemaps (consultada el 29 de octubre de 2026)
  3. W3C: XML Path Language (XPath) 1.0, pruebas de nodo (consultada el 29 de octubre de 2026)
  4. libxml2: página de manual de xmllint (consultada el 29 de octubre de 2026)
  5. Documentación de Python: xml.etree.ElementTree, analizar XML con espacios de nombres (consultada el 29 de octubre de 2026)
  6. Documentación de Python: seguridad XML (consultada el 29 de octubre de 2026)
  7. IMPORTXML - Ayuda de Editores de Documentos de Google (consultada el 29 de octubre de 2026)
  8. Prueba de XMLNest : Prueba realizada por XMLNest el 29 de octubre de 2026 con Python 3.14.3: findall('.//loc') de ElementTree devolvió 0 elementos en un sitemap con el espacio de nombres de sitemaps.org, y el script con espacio de nombres de este artículo escribió 4 filas a partir de un índice que listaba un sitemap normal y otro comprimido con gzip, con &amp; decodificado como &. lxml 6, basado en libxml2 como xmllint, también devolvió 0 nodos para //loc y todas las URL para //*[local-name()='loc']. Los comandos de xmllint no se ejecutaron; siguen su manual. (consultada el 29 de octubre de 2026)