Sitemap-URLs als CSV oder JSON extrahieren: 5 Methoden

Sitemap-URLs als CSV oder JSON extrahieren: 5 Methoden

Wer Sitemap-URLs extrahieren will, braucht nur den Text der loc-Elemente. Die Schwierigkeit ist der XML-Namespace: Eine einfache Abfrage //loc findet nichts. Hier sind fünf Wege, die Liste als CSV, JSON oder reinen Text zu erhalten, von einem Browser-Tool ohne Einrichtung bis zu einem Python-Skript, das auch einem Sitemap-Index folgt.

Warum liefert //loc bei einer Sitemap nichts?

Jede Sitemap deklariert in ihrem Wurzelelement einen Namespace: <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">. Das Protokoll von sitemaps.org schreibt ihn vor. Da es ein Standard-Namespace ohne Präfix ist, gehört jedes <url>- und <loc>-Element der Datei zu ihm.

XPath 1.0 behandelt einen Namen ohne Präfix als Namen ohne Namespace. //loc sucht also ein <loc>-Element ohne Namespace, und eine gültige Sitemap enthält keines. Die Abfrage ist korrektes XPath. Sie passt nur auf null Knoten, und deshalb liefern so viele Skripte und Formeln ein leeres Ergebnis.

Es gibt zwei Lösungen, und jede Methode unten nutzt eine davon:

  • Das Element nur über seinen lokalen Namen ansprechen: //*[local-name()='loc']. Das funktioniert in jedem XPath-1.0-Werkzeug, ohne Einrichtung.
  • Ein Präfix an die Namespace-URI binden und verwenden: //sm:loc, wobei sm für http://www.sitemaps.org/schemas/sitemap/0.9 steht. Python und die meisten Bibliotheken erlauben das.

Beides können Sie mit Ihrer eigenen Datei in unserem XPath-Tester für XML ausprobieren. Er gibt dem Standard-Namespace das Präfix d, sodass //d:loc jede URL mit ihrer Zeile auflistet.

Noch eine Falle: der Sitemap-Index. Sein Wurzelelement ist <sitemapindex>, und seine <loc>-Elemente verweisen auf andere Sitemaps, nicht auf Seiten. Google erlaubt bis zu 50.000 davon pro Index, alle auf derselben Website, sofern keine websiteübergreifende Einreichung eingerichtet ist. WordPress liefert standardmäßig einen Index aus, wie unser Leitfaden zu den Adressen der WordPress-Sitemap zeigt. Bei jeder Methode unten steht, wie sie mit einem Index umgeht.

Sitemap als CSV im Browser, ohne Installation

Unser kostenloser Sitemap-Checker liest eine Sitemap, die Sie einfügen, öffnen oder auf die Seite ziehen, als .xml oder .xml.gz. Die Datei bleibt in Ihrem Browser. Nach der Prüfung zeigt der Tab „URLs“ eine Tabelle mit vier Spalten: URL, letzte Änderung, Änderungshäufigkeit und Priorität.

  1. Öffnen Sie die Sitemap im Browser und speichern Sie sie, oder kopieren Sie ihren Quelltext.
  2. Fügen Sie ihn in den Sitemap-Checker ein oder nutzen Sie „Datei öffnen…“. Klicken Sie dann auf „Sitemap prüfen“.
  3. Öffnen Sie den Tab „URLs“. Geben Sie unter „URLs filtern“ einen Teil einer Adresse ein, um nur bestimmte Zeilen zu behalten, und klicken Sie auf eine Spaltenüberschrift, um zu sortieren.
  4. Klicken Sie auf „CSV exportieren“.

Die CSV-Datei enthält die Zeilen so, wie die Tabelle sie zeigt, mit Filter und Sortierung, und die Spalten loc, lastmod, changefreq und priority. Sie ist in UTF-8 mit Byte Order Mark kodiert, damit Excel Umlaute und Akzente richtig anzeigt. Eine Zelle, die mit =, +, - oder @ beginnt, erhält ein vorangestelltes Apostroph, damit eine Tabellenkalkulation sie nicht als Formel ausführt. Die Datei trägt den Namen der geöffneten Datei, bei eingefügtem Text heißt sie sitemap.csv.

Zwei Einschränkungen gibt es. Das Tool exportiert nur CSV, kein JSON. Und es verbindet sich nie mit anderen Websites: Es kann eine Sitemap also weder über ihre URL lesen noch den Sitemaps folgen, die ein Index aufführt. Bei einem Index heißt der Tab „Sitemaps“, und die CSV-Datei hat zwei Spalten, loc und lastmod: die Unter-Sitemaps, nicht die Seiten.

Für JSON aus einer Datei wandelt der Konverter von XML in JSON die ganze Sitemap um. Sie erhalten ihre Struktur, mit jedem <url> als Objekt, statt einer flachen Liste von Zeilen.

Live-Sitemap aus Chrome als CSV oder JSON exportieren

Die XMLNest-Erweiterung für Chrome, erhältlich im Chrome Web Store, öffnet eine Live-Sitemap-URL in Chrome und zeigt sie in ihrer Leseansicht als Tabelle. Über der Tabelle stehen drei Schaltflächen:

  • CSV exportieren speichert die Spalten URL, letzte Änderung, Änderungshäufigkeit und Priorität, dazu die Anzahl der Bilder und der alternativen Sprachversionen, wenn die Sitemap Bild- oder hreflang-Einträge hat.
  • JSON exportieren speichert ein Array von Objekten mit den Schlüsseln loc, lastmod, changefreq, priority, images und alternates.
  • URLs kopieren kopiert nur die Adressen, eine pro Zeile, bereit zum Einfügen in eine Spalte oder einen Crawler.

Alle drei verwenden die angezeigten Zeilen in der angezeigten Reihenfolge. „URLs filtern“ behält die Adressen, die einen bestimmten Text enthalten, und „Geändert seit“ behält die URLs, die an oder nach einem Datum geändert wurden. Filtern Sie nach /blog/ und wählen Sie ein Datum, dann exportieren Sie nur die neuen Beiträge. Wie im Sitemap-Checker werden CSV-Zellen maskiert, die mit =, +, - oder @ beginnen.

Bei einem Sitemap-Index liest „Alle Unter-Sitemaps laden“ die im Index aufgeführten Sitemaps derselben Website, höchstens 50, eine nach der anderen. Sitemaps auf anderen Websites ruft die Erweiterung nie ab. „Alle URLs anzeigen“ fasst sie dann in einer Tabelle zusammen, mit einer zusätzlichen Spalte „Sitemap“, die für jede URL die Herkunftsdatei nennt. Die drei Exporte funktionieren auch mit dieser Tabelle.

Sitemap-URLs mit curl und xmllint auslesen

xmllint gehört zu libxml2. macOS bringt es mit, unter Debian und Ubuntu steckt es im Paket libxml2-utils, und unter Windows können Sie es über WSL nutzen. Die Option --xpath führt einen Ausdruck aus und gibt das Ergebnis aus:

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

Das - weist xmllint an, aus der Pipe zu lesen. Mit //loc gibt xmllint laut Handbuch „XPath set is empty“ aus und endet mit dem Code 11: Das ist das oben beschriebene Namespace-Problem. Das Handbuch nennt keine Option, mit der sich für --xpath ein Namespace-Präfix binden lässt. Deshalb ist hier die Form mit local-name() die praktische.

Das Handbuch sagt nur, dass jeder Knoten des Ergebnisses vollständig serialisiert wird. Prüfen Sie, ob Ihre Version eine URL pro Zeile ausgibt, bevor Sie urls.txt an ein anderes Skript weitergeben.

Eine gzip-komprimierte Sitemap braucht einen Schritt mehr, gunzip, zwischen dem Download und xmllint:

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

Bei einem Index liefert derselbe Befehl die Adressen der Unter-Sitemaps. Führen Sie ihn für jede davon erneut aus, um die Seiten zu erhalten:

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

Das Python-Beispiel unten haben wir ausgeführt; die xmllint-Befehle folgen dem Handbuch.

Python: Sitemap als JSON oder CSV, Indexdateien inklusive

ElementTree, der XML-Parser von Python, schreibt einen Standard-Namespace in den Tag-Namen. Das Wurzelelement einer Sitemap heißt also {http://www.sitemaps.org/schemas/sitemap/0.9}urlset. In unserem Test mit Python 3.14 lieferte root.findall(".//loc") null Elemente. Die Python-Dokumentation zeigt die Lösung: Übergeben Sie find() und findall() ein Dictionary mit Präfixen.

Dieses Skript liest eine Sitemap oder einen Index über die URL, folgt den Unter-Sitemaps eines Index, entpackt gzip-Dateien und schreibt loc, lastmod und die Quell-Sitemap in 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")

Starten Sie es mit python sitemap_to_csv.py https://www.example.com/sitemap_index.xml. Unser Testindex führte eine normale und eine .xml.gz-Sitemap auf, und das Skript schrieb vier Zeilen. Eine URL, die im XML als ?page=2&amp;sort=new stand, kam als ?page=2&sort=new heraus, weil der Parser Entitäten dekodiert.

Für JSON ersetzen Sie den letzten Block durch json.dump() aus der Standardbibliothek, zum Beispiel mit einer Liste von Objekten mit den Schlüsseln loc, lastmod und sitemap. Wenn Sie Sitemaps von Websites verarbeiten, die Sie nicht kontrollieren, lesen Sie zuerst den Abschnitt zur XML-Sicherheit in der Python-Dokumentation.

Google Sheets IMPORTXML: eine URL-Liste in einer Spalte

Die Google-Hilfeseite zu IMPORTXML nennt XML unter den Datentypen, die die Funktion liest. Die Syntax lautet IMPORTXML(url, xpath_query, locale), und die URL kann ein Zellbezug sein. Verwenden Sie in einer Zelle die Form mit local-name():

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

Zu XML-Namespaces sagt die Hilfeseite nichts. Der Ausdruck mit local-name() hängt nicht davon ab, wie Sheets sie behandelt, weil er das Element allein über seinen Namen findet. Über Datei > Herunterladen > Kommagetrennte Werte (.csv) speichern Sie das Tabellenblatt anschließend als CSV-Datei. IMPORTXML haben wir für diesen Artikel nicht getestet; dieser Abschnitt folgt der Google-Hilfe.

Bei einem Index liefert die Formel die Unter-Sitemaps. Schreiben Sie je eine dieser Adressen in eine Zelle der Spalte A und verwenden Sie dann für jede =IMPORTXML(A2, "//*[local-name()='loc']") in der Spalte daneben.

Lädt die Sitemap selbst nicht, beginnen Sie mit unserem Artikel zum Status Sitemap konnte nicht abgerufen werden: Eine HTML-Fehlerseite enthält überhaupt kein <loc>-Element.

Quellen

  1. sitemaps.org: Protokoll (geprüft am 29. Oktober 2026)
  2. Google Search Central: Sitemaps mit Sitemap-Indexdateien verwalten (geprüft am 29. Oktober 2026)
  3. W3C: XML Path Language (XPath) 1.0, Knotentests (geprüft am 29. Oktober 2026)
  4. libxml2: Handbuchseite von xmllint (geprüft am 29. Oktober 2026)
  5. Python-Dokumentation: xml.etree.ElementTree, XML mit Namespaces parsen (geprüft am 29. Oktober 2026)
  6. Python-Dokumentation: XML-Sicherheit (geprüft am 29. Oktober 2026)
  7. IMPORTXML - Google Docs-Editoren-Hilfe (geprüft am 29. Oktober 2026)
  8. Test von XMLNest : Getestet von XMLNest am 29. Oktober 2026 mit Python 3.14.3: ElementTree findall('.//loc') lieferte 0 Elemente bei einer Sitemap mit dem Namespace von sitemaps.org, und das Skript mit Namespace aus diesem Artikel schrieb 4 Zeilen aus einem Index, der eine normale und eine gzip-komprimierte Sitemap aufführte, wobei &amp; zu & dekodiert wurde. lxml 6, das wie xmllint auf libxml2 aufbaut, lieferte für //loc ebenfalls 0 Knoten und für //*[local-name()='loc'] jede URL. Die xmllint-Befehle wurden nicht ausgeführt; sie folgen dem Handbuch. (geprüft am 29. Oktober 2026)