Scraping

Sitemaps als Entdeckungsquelle: Jede URL finden, ohne die gesamte Website zu crawlen

XML-Sitemaps listen die URLs einer Website auf, manchmal auch, wann sie geändert wurden. Wie man sie richtig liest und warum man lastmod niemals ohne Überprüfung vertrauen sollte.

James Meadow

James Meadow

29. September 2026 · 9 Min. Lesezeit

Die meisten Crawler entdecken Seiten auf die teure Art: eine Seite abrufen, ihre Links extrahieren, sie in die Warteschlange stellen, wiederholen. Das funktioniert, verbraucht aber den Großteil des Budgets für Navigation, Listenseiten und Seiten, die man bereits gesehen hat, und kann trotzdem Seiten übersehen, auf die nichts verlinkt.

Viele Websites veröffentlichen absichtlich eine Liste ihrer URLs. XML-Sitemaps existieren, damit Suchmaschinen Seiten effizient finden können, und dieselbe Datei sagt einem Datenteam, was auf einer Website existiert, wie es organisiert ist und manchmal, was sich kürzlich geändert hat. Dieser Leitfaden behandelt, wie man Sitemaps richtig findet und liest, und warum das nützlichste Feld darin, lastmod, vor dem Vertrauen geprüft werden muss. Wir haben es auf zwei realen Websites geprüft, und es ist auf zwei verschiedene Arten fehlgeschlagen.

Die wichtigsten Punkte

  • Eine Sitemap listet bis zu 50.000 URLs pro Datei, und ein Sitemap-Index kann bis zu 50.000 Sitemaps auflisten, sodass selbst sehr große Websites ein vollständiges Inventar veröffentlichen können.
  • Sitemaps werden üblicherweise in der robots.txt mit einer Sitemap:-Zeile deklariert; dort nachsehen, bevor man einen Speicherort errät.
  • lastmod ist das Feld, das die meisten Abrufe einsparen könnte, und es ist das am wenigsten zuverlässige. Google gibt an, lastmod nur zu verwenden, wenn es “durchgängig und überprüfbar” korrekt ist.
  • In unserer Prüfung gab die News-Sitemap eines großen Nachrichtenverlags jedem Eintrag denselben Zeitstempel, den Moment, in dem die Datei generiert wurde. Ein großes Dokumentenarchiv veröffentlichte über 10.000 URLs ohne jegliches lastmod.
  • Sitemaps zur Entdeckung nutzen, lastmod pro Website verifizieren, bevor man darauf plant, und das Crawlen über Links als Sicherheitsnetz behalten.

Was das Protokoll erlaubt

Das Sitemap-Protokoll ist kurz und lohnt sich, es genau zu kennen:

RegelDetail
Größenbeschränkungen”nicht mehr als 50.000 URLs” und “nicht größer als 50MB (52.428.800 Bytes)” pro Sitemap-Datei, unkomprimiert
Sitemap-IndizesEine Datei, die andere Sitemaps auflistet, mit denselben Grenzen von 50.000 Einträgen und 50MB
KomprimierungDateien dürfen gzip-komprimiert sein, solange sie unkomprimiert innerhalb der Grenze liegen
lastmodOptional, im W3C-Datetime-Format, das auch nur ein Datum sein kann, etwa YYYY-MM-DD
EntdeckungEine Sitemap:-Zeile in der robots.txt; eine Website kann mehrere auflisten

Zwei optionale Felder, die man sehen wird, changefreq und priority, können ignoriert werden. Google sagt unmissverständlich, dass es “die Werte <priority> und <changefreq> ignoriert”, und es gibt wenig Grund, warum jemand anderes ihnen vertrauen sollte.

News-Websites veröffentlichen oft eine separate News-Sitemap, die nur aktuelle Artikel abdeckt, mit zusätzlichen Feldern wie einem Veröffentlichungsdatum. Diese sind klein, werden häufig neu generiert und sind sehr nützlich zur Überwachung aktueller Inhalte.

Sitemaps richtig lesen

Ein robuster Reader muss vier Dinge tun: die Sitemaps aus der robots.txt finden, gzip verarbeiten, Sitemap-Indizes rekursiv verfolgen und lastmod in etwas Vergleichbares parsen. Er sollte außerdem begrenzen, wie viele Dateien er abruft, da ein Index auf Tausende davon verweisen kann.

import gzip
import io
import re
import urllib.request
from datetime import datetime, timezone

from defusedxml.ElementTree import fromstring  # pip install defusedxml

NS = "{http://www.sitemaps.org/schemas/sitemap/0.9}"
UA = "Mozilla/5.0 (compatible; sitemap-reader)"
MAX_BYTES = 52_428_800  # the protocol's 50MB limit, uncompressed


def fetch(url, opener=None):
    opener = opener or urllib.request.build_opener()
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    with opener.open(req, timeout=45) as r:
        body = r.read(MAX_BYTES + 1)
    if body[:2] == b"\x1f\x8b":
        body = gzip.GzipFile(fileobj=io.BytesIO(body)).read(MAX_BYTES + 1)
    if len(body) > MAX_BYTES:
        raise ValueError(f"{url} exceeds the 50MB sitemap limit")
    return body


def sitemap_roots(origin, opener=None):
    """Sitemaps declared in robots.txt, falling back to /sitemap.xml."""
    try:
        robots = fetch(origin.rstrip("/") + "/robots.txt", opener).decode("utf-8", "replace")
        found = re.findall(r"(?im)^\s*sitemap:\s*(\S+)", robots)
    except OSError:
        found = []
    return found or [origin.rstrip("/") + "/sitemap.xml"]


def parse_lastmod(value):
    if not value:
        return None
    value = value.strip().replace("Z", "+00:00")
    try:
        dt = datetime.fromisoformat(value)
    except ValueError:
        return None
    return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)


def walk(url, opener=None, max_files=20, _seen=None):
    """Yield (loc, lastmod) from a sitemap or sitemap index, following nested indexes."""
    seen = _seen if _seen is not None else set()
    if url in seen or len(seen) >= max_files:
        return
    seen.add(url)
    root = fromstring(fetch(url, opener))
    if root.tag == NS + "sitemapindex":
        children = sorted(root.findall(NS + "sitemap"),
                          key=lambda s: parse_lastmod(s.findtext(NS + "lastmod")) or datetime.min.replace(tzinfo=timezone.utc),
                          reverse=True)
        for child in children:
            yield from walk(child.findtext(NS + "loc").strip(), opener, max_files, seen)
    else:
        for u in root.findall(NS + "url"):
            yield u.findtext(NS + "loc").strip(), parse_lastmod(u.findtext(NS + "lastmod"))


def changed_since(origin, since, opener=None, max_files=20):
    """URLs whose sitemap lastmod is newer than `since`, plus those with no lastmod at all."""
    changed, undated, total = [], [], 0
    for root in sitemap_roots(origin, opener):
        for loc, lastmod in walk(root, opener, max_files):
            total += 1
            if lastmod is None:
                undated.append(loc)
            elif lastmod > since:
                changed.append((loc, lastmod))
    return {"total": total, "changed": changed, "undated": undated}

Der Index-Walker besucht Kind-Sitemaps zuerst in der Reihenfolge der neuesten, dort wo der Index Daten liefert, sodass ein begrenzter Lauf zuerst die zuletzt aktualisierten Teile einer großen Website liest. Zwei Details sind aus Sicherheitsgründen vorhanden. Eine Sitemap ist nicht vertrauenswürdige Eingabe vom Server einer anderen Partei, daher parst der Code sie mit defusedxml, das die Entity-Tricks ablehnt, mit denen standardmäßige XML-Parser eine kleine Datei auf Gigabyte expandieren lassen können, und er begrenzt sowohl den Download als auch die entpackte Größe auf das 50MB-Limit des Protokolls, sodass eine komprimierte Datei nicht unbegrenzt anwachsen kann. Der Parameter opener erlaubt es, Anfragen über einen Proxy zu leiten, wenn man eine marktspezifische Version einer Website sehen muss, derselbe Ansatz wie bei jedem anderen Abruf.

lastmod erst nach Prüfung vertrauen

lastmod verspricht genau das, was inkrementelle Datenerfassung braucht: eine Liste dessen, was sich seit dem letzten Lauf geändert hat. Wäre es überall zuverlässig, könnte ein Crawler nur die geänderten Seiten abrufen und alles andere überspringen. Es ist nicht überall zuverlässig, und unsere beiden Testwebsites zeigen beide gängigen Fehlschläge.

Fehler eins: lastmod ist der Generierungszeitpunkt. Wir haben die News-Sitemap eines großen Nachrichtenverlags gelesen. Jeder datierte Eintrag trug einen von zwei Zeitstempeln, eine Sekunde voneinander entfernt: den Moment, in dem die Datei generiert wurde, nicht den Moment, in dem sich jeder Artikel geändert hat. Das Filtern nach “in den letzten sechs Stunden geändert” ergab jede URL in der Datei. Naiv verwendet, würde dieses lastmod bei jedem Lauf ein erneutes Abrufen von allem auslösen.

Fehler zwei: kein lastmod vorhanden. Wir haben die Sitemap eines großen technischen Dokumentenarchivs gelesen. Es listete 10.236 URLs auf, und keine trug ein lastmod. Das ist eine durchaus gute Quelle zur Entdeckung und überhaupt keine Hilfe bei der Entscheidung, was erneut abgerufen werden soll.

Deshalb sagt Googles eigene Anleitung, dass es lastmod nur verwendet, “wenn es durchgängig und überprüfbar (zum Beispiel durch Vergleich mit der letzten Änderung der Seite) korrekt ist”, und deshalb sollte man denselben Test selbst anwenden. Bevor man auf das lastmod einer Website plant:

  1. Die Streuung prüfen. Wenn die meisten Einträge einen Zeitstempel teilen oder die Werte dem Generierungszeitpunkt der Datei folgen, beschreibt das Feld keine Seitenänderungen.
  2. Stichprobe nehmen und vergleichen. Eine Stichprobe von Seiten abrufen und lastmod mit einem unabhängigen Signal vergleichen, etwa dem eigenen dateModified der Seite in strukturierten Daten oder einem Content-Fingerprint. Wie man dateModified extrahiert, wird in stop parsing HTML behandelt, und wie man Inhalte vergleicht, ohne im Rauschen zu versinken, in change detection at scale.
  3. Die Website bewerten. Pro Website festhalten, wie oft sich lastmod geändert hat, als sich der Inhalt änderte, und wie oft sich der Inhalt änderte, ohne dass sich lastmod änderte. lastmod für die Planung nur auf Websites verwenden, die diesen Test bestehen, und weiter prüfen, denn der Sitemap-Generator einer Website kann sich ohne Vorwarnung ändern.

Wo Sitemaps in eine Datenerfassungs-Pipeline passen

Sitemaps sind am stärksten als erster Schritt, nicht als einziger:

  • Entdeckung. Eine Sitemap liefert das Inventar direkt, einschließlich Seiten, die schlecht verlinkt sind. Für Katalog- und Content-Websites ist es oft die vollständigste verfügbare URL-Liste.
  • Segmentierung. Sitemaps sind häufig nach Inhaltstyp oder Bereich aufgeteilt, etwa Produkte, Kategorien, Artikel und Videos. Die Aufteilung zeigt, wie eine Website organisiert ist, bevor man auch nur eine Seite abruft.
  • Inkrementelle Datenerfassung, auf Websites, deren lastmod die obigen Prüfungen besteht, und über News-Sitemaps für aktuelle Artikel.
  • Abdeckungsprüfungen. Der Vergleich dessen, was der Crawler gefunden hat, mit dem, was die Sitemap auflistet, zeigt, was fehlt.

Das linkbasierte Crawlen als Sicherheitsnetz behalten. Sitemaps können veraltet, unvollständig oder absichtlich auf das beschränkt sein, was eine Website indexiert haben möchte. Und die robots.txt der Website für die Seiten selbst respektieren: Eine in einer Sitemap erscheinende URL ist eine Einladung an Suchmaschinen, sie zu indexieren, kein Verzicht auf irgendetwas anderes, um das die Website gebeten hat, wie in robots.txt, AI opt-outs and reservation signals behandelt.

So genutzt, senken Sitemaps das Abruf-Budget an den beiden Stellen, an denen es üblicherweise verschwendet wird: bei der Navigation, um Seiten zu finden, und beim erneuten Abrufen von Seiten, die sich nicht geändert haben. Die zweite Einsparung hängt vollständig davon ab, dass lastmod vertrauenswürdig ist, weshalb cost-aware crawl scheduling ein unverifiziertes lastmod als Hinweis behandeln sollte, nicht als Tatsache.

Fazit

Sitemaps sind das günstigste URL-Inventar im Web: eine Website, die einem in einem Standardformat mitteilt, was sie gefunden haben möchte. Sie aus der robots.txt lesen, gzip und verschachtelte Indizes verarbeiten, begrenzen, was man abruft, und sie zuerst zur Entdeckung nutzen.

Dann lastmod so behandeln, wie Google es tut: nur nützlich, wenn nachgewiesen wurde, dass es für diese Website korrekt ist. Bei einer geprüften Website war es lediglich der Zeitpunkt der Dateigenerierung. Bei einer anderen existierte es überhaupt nicht. Bei einer Website, auf der es sich als verlässlich erweist, kann es das erneute Abrufen drastisch senken. Der einzige Weg zu wissen, mit welcher Art von Website man es zu tun hat, ist die Prüfung.

Quellen und Referenzen

Bereit, loszulegen?

Testen Sie Shifters Residential-Proxys, 205M+ IPs, 195+ Länder, ab 0,75 $/GB.

Jetzt starten