Geonode logo
Geonode Team

Geonode Team

Aktualisiert: 7. Oktober 2026

Veröffentlicht: 02.09.2026

XPath „preceding-sibling“: So funktioniert es (mit Beispielen)

`preceding-sibling` wählt Knoten aus, die auf derselben Ebene des Baums vor dem Kontextknoten liegen. Ganz einfach – bis man „`preceding-sibling::td[1]`“ schreibt und eine andere Zelle erhält als erwartet. Der Grund dafür ist, dass „`preceding-sibling`“ eine umgekehrte Achse ist und die Positionsnummerierung auf einer umgekehrten Achse rückwärts verläuft. Dieser Leitfaden behandelt, was ausgewählt wird, die Fallstricke bei der Nummerierung, wie sich die Funktion von ``preceding`` unterscheidet und welche Extraktionsmuster damit gelöst werden können.

Unser Anliegen, ganz klar formuliert: Wir sind Geonode und verkaufen Proxys an Leute, die Web-Scraping betreiben, daher steht XPath in direktem Zusammenhang mit unserem Geschäft. Es ist wichtig zu betonen, dass ein fehlerhafter Selektor niemals ein Proxy-Problem ist. Wenn Ihre Datenextraktion nicht mehr funktioniert, hat die Website ihr Markup geändert, und weder Bandbreite noch Adressrotation können das beheben. Die beiden Probleme werden oft verwechselt, da sich beide so äußern: „Mein Scraper liefert keine Daten mehr“ – doch ein Proxy-Fehler führt zu blockierten Antworten und Fehlerseiten, während ein Selektor-Fehler erfolgreiche Antworten liefert, die jedoch keine Daten enthalten. Überprüfen Sie zuerst den rohen HTML-Code, bevor Sie irgendetwas anderes überprüfen. Wenn die Seite vorhanden ist und Ihr XPath eine leere Knotenmenge zurückgibt, ist dieser Artikel relevant und Ihr Proxy funktioniert einwandfrei.

Was „preceding-sibling“ tatsächlich auswählt

Die XPath 1.0-Spezifikation definiert dies in einer Zeile: Die Achse „preceding-sibling“ „enthält alle vorangehenden Geschwister des Kontextknotens; ist der Kontextknoten ein Attributknoten oder ein Namensraumknoten, ist die Achse „preceding-sibling“ leer“.

Zwei Begriffe sind dabei entscheidend. Geschwisterknoten bezeichnet Knoten, die denselben übergeordneten Knoten haben – keine Cousins, keine Vorfahren, nichts auf einer anderen Ebene. Vorangehend bedeutet „früher in der Dokumentreihenfolge“.

<div>
  <p>First</p>
  <p>Second</p>
  <span id="here">Context</span>
  <p>Third</p>
</div>

Mit dem Knoten „span“ als Kontextknoten:

preceding-sibling::p        → First, Second
following-sibling::p        → Third
preceding-sibling::*        → both p elements

Die Einschränkung bezüglich Attributknoten sollte man im Hinterkopf behalten, da sie eine Klasse leerer Ergebnisse erklärt. Wenn Sie zu einem Attribut navigiert sind – //@class –, dann ist „preceding-sibling“ von dort aus per Definition leer, unabhängig davon, was das Element umgibt, zu dem das Attribut gehört. Attribute sind keine Geschwister von irgendetwas.

Die „Reverse-Axis“-Falle: „[1]

“ bedeutet nicht „First“ Dies ist die mit Abstand häufigste Ursache für falsche Ergebnisse, und die Spezifikation erklärt genau, warum das so ist.

XPath stuft „ancestor “, „ancestor-or-self “, „preceding “ und „preceding-sibling “ als Reverse-Axes ein. Bei Reverse-Axes wird die Position der Nähe durch die Anordnung der Knoten in umgekehrter Dokumentreihenfolge bestimmt.

Bei preceding-sibling ist Position 1 also das nächstgelegene vorangehende Geschwisterelement – dasjenige, das unmittelbar vor dem Kontextknoten liegt – und nicht das erste im Dokument.

<div>
  <p>Alpha</p>
  <p>Beta</p>
  <p>Gamma</p>
  <span id="here">Context</span>
</div>
preceding-sibling::p[1]     → Gamma   (nearest)
preceding-sibling::p[2]     → Beta
preceding-sibling::p[3]     → Alpha   (furthest)
(preceding-sibling::p)[1]   → Alpha   (first in document order)

Die Klammern ändern alles. Ohne sie ist [1] ein Prädikat, das entlang der umgekehrten Achse angewendet wird und „nächstgelegen“ bedeutet. Mit ihnen wird das Ergebnis der Achse zunächst in einer Knotenmenge in Dokumentreihenfolge gesammelt, und „[1] “ verweist darauf, was „erster“ bedeutet.

Vergleichen Sie dies mit following-sibling , einer Vorwärtsachse, bei der beide Fälle übereinstimmen:

following-sibling::p[1]     → the next one
(following-sibling::p)[1]   → also the next one

Diese Asymmetrie ist der Grund, warum Personen, die mit following-sibling gelernt haben, bei preceding-sibling ins Straucheln geraten. Die Gewohnheit bleibt bestehen, die Semantik jedoch nicht.

In der Praxis ist die Form ohne Klammern fast immer die gewünschte. „Das Label unmittelbar vor diesem Wert“ ist die übliche Anforderung, und das ist preceding-sibling::label[1] . Greifen Sie nur dann zu Klammern, wenn Sie wirklich „das erste im Dokument“ meinen – was seltener vorkommt, als es klingt.

„preceding-sibling“ im Vergleich zu „preceding“

Zwei verschiedene Achsen mit verwirrend ähnlichen Namen – und der Unterschied ist entscheidend.

Die Spezifikation definiert „preceding“ als „alle Knoten im selben Dokument wie der Kontextknoten, die in der Dokumentreihenfolge vor dem Kontextknoten liegen, unter Ausschluss aller Vorfahren sowie von Attributknoten und Namespace-Knoten“.

„preceding“ umfasst also alles, was im Dokument in beliebiger Tiefe davor liegt, abzüglich der Vorfahren. „preceding-sibling“ umfasst nur diejenigen, die denselben Elternknoten haben.

<body>
  <header><h1>Title</h1></header>
  <div>
    <p>One</p>
    <span id="here">Context</span>
  </div>
</body>

Aus dem „span“:

preceding-sibling::*    → the p only
preceding::*            → the p, the h1, and the header

Der Ausschluss von Vorfahren ist der Teil, der viele überrascht. Das „div“, das den „span“-Tag enthält, ist nicht in „preceding“ enthalten, obwohl sein öffnender Tag im Quelltext früher erscheint. Vorfahren werden per Definition ausgeschlossen, da es bei der Achse darum geht, was vor Ihnen kam, und nicht darum, was Sie enthält.

Wann was zu verwenden ist. „preceding-sibling“ für strukturierte Beziehungen – eine Beschriftung und ihr Wert, eine Überschrift und der darunterliegende Absatz, Zellen in einer Zeile. „preceding“ für wirklich lose Beziehungen, wie beispielsweise „die nächstgelegene Überschrift irgendwo oberhalb dieses Elements, unabhängig von der Verschachtelung“. „preceding“ ist umfassender, deutlich langsamer und es ist viel wahrscheinlicher, dass dabei etwas gefunden wird, das Sie gar nicht beabsichtigt haben.

Das Label-Wert-Muster

Das ist der Grund, warum es beim Scraping das „preceding-sibling

“ gibt, und es lohnt sich, dieses Muster gründlich zu verinnerlichen, da die meisten realen Markups eine Variante davon sind.

Das Problem: Man möchte einen Wert, der nur anhand der daneben stehenden Bezeichnung identifiziert werden kann. Der Wert selbst hat keine aussagekräftige Klasse, keine ID, nichts, was ihn auszeichnet.

Definitionslisten:

<dl>
  <dt>Price</dt>
  <dd>£42.00</dd>
  <dt>Stock</dt>
  <dd>In stock</dd>
</dl>

Den Preis zu ermitteln bedeutet, die „dd

“ zu finden, deren unmittelbar vorangehendes „dt

“ den Text „Price“ enthält:

//dd[preceding-sibling::dt[1] = 'Price']

Lesen Sie das von innen nach außen: Für jedes „dd

“ nehmen Sie dessen unmittelbar vorangehendes „dt

“; behalten Sie die „dd

“, wenn dieser Text „Price“ lautet. Beachten Sie, dass die [1]

unerlässlich ist. Ohne sie wäre preceding-sibling::dt = 'Price'

wahr, wenn irgendein vorangehendes dt

übereinstimmt, sodass auch das zweite dd

in Frage käme. Das ist ein echter und häufig auftretender Fehler.

Tabellenzellen:

<tr>
  <td>SKU</td>
  <td>ABC-123</td>
</tr>
//td[preceding-sibling::td[1] = 'SKU']

Oder aus der Kopfzeile über eine Zeile einer zweispaltigen Spezifikationstabelle:

//th[normalize-space() = 'Weight']/following-sibling::td[1]

Die zweite Form ist in der Regel vorzuziehen, wenn die Beschriftung ein „th

“ ist, da sie sich vorwärts liest und der Struktur der Tabelle entspricht.

Verbesserungen der Robustheit, die dafür sorgen, dass diese Ausdrücke auch in realem Markup funktionieren:

//dd[preceding-sibling::dt[1][normalize-space() = 'Price']]

normalize-space()

reduziert interne Leerzeichen und kürzt die Enden, wodurch das „pretty-printed“ HTML verarbeitet wird, das andernfalls einen exakten Zeichenfolgenvergleich unterbrechen würde. Es ist die wertvollste Funktion beim Scraping mit XPath.

Bei Teilübereinstimmungen ist contains()

toleranter und entsprechend weniger präzise:

//dd[preceding-sibling::dt[1][contains(., 'Price')]]

Vorsicht: contains(., 'Price')

findet auch Übereinstimmungen mit „Preis ohne MwSt.“ und „Historischer Preis“. Wenn die Seite mehrere solcher Beschriftungen enthält, erhalten Sie mehrere Ergebnisse, und Ihr Code wählt stillschweigend das erste aus.

Überschriften und nachfolgender Inhalt, was dasselbe Muster in umgekehrter Richtung ist:

//h2[normalize-space() = 'Specifications']/following-sibling::table[1]

Die erste Tabelle nach dieser Überschrift. Dies ist eine wirklich häufige Anforderung auf Dokumentations- und Produktseiten, und es gibt kein CSS-Äquivalent, das „die erste Tabelle nach dieser bestimmten Überschrift“ ausdrückt.

Kombination mit Prädikaten und Bedingungen „

preceding-sibling

“ lässt sich mit den übrigen XPath-Elementen kombinieren, und einige Kombinationen sind besonders nützlich.

Zählen von Geschwistern — nützlich, um das erste oder letzte Element zu finden oder die Position zu überprüfen:

//li[count(preceding-sibling::li) = 0]     first li
//li[count(preceding-sibling::li) < 3]     first three

Vorhandensein prüfen — eine Knotenmenge ist in einem booleschen Kontext wahr, wenn sie nicht leer ist:

//p[preceding-sibling::h2]                 paragraphs with an h2 somewhere before
//p[not(preceding-sibling::p)]             first paragraph among its siblings

Achsen verketten:

//span[@class='value']/preceding-sibling::*[1]/text()

Das unmittelbar vorhergehende Element, unabhängig vom Tag, und dessen Text.

Mehrere Bedingungen:

//td[preceding-sibling::td[1] = 'Status'][normalize-space() != '']

Zwei nacheinander angewendete Prädikate: die Zelle nach einer „Status“-Beschriftung und nicht leer.

Ein Hinweis zur Abkürzung „..

“, die oft übersichtlicher ist als eine Achse:

//dt[.='Price']/following-sibling::dd[1]

ist für dasselbe Ergebnis in der Regel besser lesbar als die Form „preceding-sibling

“ und folgt der Schreibrichtung des Dokuments. Bevorzugen Sie diese Form, wenn der Anker die Beschriftung ist.

Wo CSS-Selektoren es ersetzen können und wo nicht

Die gängige Meinung, dass „CSS nicht rückwärts blicken kann“, muss aktualisiert werden.

CSS kann nun mit „:has()“ Geschwisterbedingungen bilden. Diese Funktion wird von modernen Browsern weitgehend unterstützt und ermöglicht es, einen Selektor von einer Geschwisterbeziehung abhängig zu machen:

dt:has(+ dd)          a dt immediately followed by a dd
li:has(~ li.active)   an li with a later sibling that is active

Was CSS noch nicht kann:

Auswahl nach Textinhalt. Es gibt kein CSS-Äquivalent zu „[text() = 'Price']“. Allein aus diesem Grund bleibt das Label-Wert-Muster eine Domäne von XPath, da der Abgleich von Labels ein Textabgleich ist.

Navigation zu einem beliebigen Vorfahren. „:has()“ bietet eine Form der Auswahl von übergeordneten Elementen, aber es gibt keine allgemeine Vorfahrenachse.

Entlang einer umgekehrten Achse indizieren. Es gibt kein CSS-Konstrukt, das „das nächstgelegene vorhergehende Element dieses Typs“ bedeutet.

Und was CSS besser kann: alles, was einfach ist. Klassen- und ID-Auswahl, Nachkommenbeziehungen, Attributabgleich. CSS-Selektoren sind lesbarer, werden von Tools besser unterstützt und sind in den meisten Engines schneller.

Die sinnvolle Vorgehensweise ist, standardmäßig CSS zu verwenden und nur dann auf XPath zurückzugreifen, wenn Sie Textabgleich oder Rückwärtsnavigation benötigen. Eine Mischung beider Ansätze in einer Codebasis ist in Ordnung, und ein Scraper, der CSS für 90 % seiner Selektoren und XPath für die schwierigen 10 % nutzt, ist einfacher zu warten als einer, der sich ausschließlich auf eine der beiden Methoden beschränkt.

Leistung und Anfälligkeit

Zwei praktische Einschränkungen.

Leistung. „preceding-sibling“ ist durch die Anzahl der Geschwisterknoten begrenzt, die in der Regel gering ist – das ist in Ordnung. „preceding“ durchsucht alles vor dem Kontextknoten im Dokument, was bei einer großen Seite aufwendig ist und innerhalb einer Schleife quadratisch skaliert. Wenn ein Selektor, der preceding verwendet, langsam ist, liegt das mit ziemlicher Sicherheit daran. Die übliche Abhilfe besteht darin, ihn mithilfe von preceding-sibling ausgehend von einem näher gelegenen Kontextknoten neu zu schreiben.

Anfälligkeit. Auf Geschwistern basierende Selektoren sind von der Dokumentstruktur abhängig – genau das, was bei einer Neugestaltung geändert wird. preceding-sibling::td[1] funktioniert nicht mehr, sobald jemand eine Spalte einfügt. Es wird kein Fehler gemeldet; der Selektor passt auf eine andere Zelle und Ihre Daten sind stillschweigend falsch.

Drei Maßnahmen, die tatsächlich helfen:

Verwenden Sie nach Möglichkeit Text statt Position als Anker. //dt[.='Price']/following-sibling::dd[1] übersteht eine Neuanordnung der Liste. (//dd)[3] hingegen nicht.

Überprüfen Sie, was Sie extrahieren. Wenn ein Preis einem Währungsmuster entsprechen soll, überprüfen Sie dies. Ein Selektor, der plötzlich den Lagerbestand statt des Preises zurückgibt, bleibt unbemerkt, solange niemand die Datenform validiert.

Bevorzugen Sie eingebettete strukturierte Daten, sofern vorhanden. Wenn die Seite JSON-LD in einem „<script type="application/ld+json">“-Block enthält, analysieren Sie stattdessen diesen. Er ist für die maschinelle Lesbarkeit konzipiert, weitaus stabiler bei Neugestaltungen und beseitigt das gesamte Problem der Anfälligkeit von Selektoren. Es lohnt sich, dies zu überprüfen, bevor Sie irgendeinen XPath schreiben – das dauert nur dreißig Sekunden.

Unbemerkte strukturelle Fehler gehören zur gleichen Problemklasse, die wir in Warum das Testen von Proxys wichtig ist beschrieben haben – die Anfrage ist erfolgreich, das Parsen ist erfolgreich, und die Daten sind falsch.

Wann man es nicht verwenden sollte

Wenn das Element über einen nutzbaren Bezeichner verfügt. Wenn eine ID, eine Klasse oder ein Datenattribut vorhanden ist, sollte man diese verwenden. Ein Selektor, der von der Struktur abhängt, ist grundsätzlich anfälliger als einer, der auf einem Namen basiert, den der Entwickler bewusst gewählt hat.

Wenn strukturierte Daten verfügbar sind. JSON-LD, Mikrodaten, eine JSON-Nutzlast hinter einem XHR. Jede dieser Möglichkeiten ist besser als das Parsen von gerendertem HTML.

Wenn die Beziehung wirklich locker ist. Wenn Sie feststellen, dass Sie „preceding::*[5]“ schreiben, sagt Ihnen die Struktur eigentlich nichts aus, und der Selektor wird beim nächsten Deployment nicht mehr funktionieren. Überdenken Sie den Ansatz.

Wenn CSS ausreicht. Für einfache Selektionen ist CSS lesbarer und wird besser unterstützt. Reserviere XPath für Textabgleiche und die Rückwärtsnavigation.

Wenn du dich auf XPath-2.0-Funktionen in einem Browser verlässt. Browser implementieren XPath 1.0 über document.evaluate, und Selenium folgt dem Browser. Das bedeutet: kein „matches()“, keine regulären Ausdrücke, kein „upper-case()“ und keine Sequenztypen. Auch serverseitige Bibliotheken wie lxml verwenden für die allgemeine API XPath 1.0. Wenn ein Code-Schnipsel, den Sie online gefunden haben, nicht funktioniert, prüfen Sie, ob er eine Funktion verwendet, die erst in einer späteren Version vorhanden ist.

Häufig gestellte Fragen

Was bewirkt „preceding-sibling“ in XPath?

Es wählt alle Knoten aus, die denselben Elternknoten wie der Kontextknoten haben und in der Dokumentreihenfolge vor diesem erscheinen. Es umfasst keine Vorfahren, Nachkommen oder Knoten auf anderen Ebenen des Baums – nur Geschwisterknoten. Es ist leer, wenn der Kontextknoten ein Attribut- oder Namespace-Knoten ist.

Warum liefert „preceding-sibling[1]“ das falsche Element?

Weil „preceding-sibling“ eine umgekehrte Achse ist und die Positionsnummerierung auf umgekehrten Achsen in umgekehrter Dokumentreihenfolge verläuft. „[1]“ bedeutet daher das nächstgelegene vorhergehende Geschwisterelement, nicht das erste im Dokument. Um das erste in der Dokumentreihenfolge zu erhalten, setzen Sie die Achse in Klammern: „(preceding-sibling::p)[1]“.

Was ist der Unterschied zwischen „preceding“ und „preceding-sibling“? „

preceding-sibling“ umfasst nur Knoten mit demselben Elternelement. „preceding“ umfasst jeden Knoten, der im Dokument früher auftritt, unabhängig von der Tiefe, wobei Vorfahren und Attributknoten ausgeschlossen sind. „preceding“ ist weitaus umfassender, deutlich langsamer und es ist viel wahrscheinlicher, dass dabei unbeabsichtigte Treffer entstehen.

Wie wähle ich in XPath einen Wert anhand seiner Bezeichnung aus?

Suchen Sie die Bezeichnung anhand des Textes und wählen Sie dann das benachbarte Element aus: //dt[normalize-space()='Price']/following-sibling::dd[1], oder von der Werteseite aus: //dd[preceding-sibling::dt[1]='Price']. Das Attribut „[1]“ ist entscheidend – ohne dieses Attribut ist das Prädikat wahr, sobald eine beliebige vorhergehende Bezeichnung übereinstimmt.

Können CSS-Selektoren das leisten, was „preceding-sibling“ tut?

Teilweise. „:has()“ bietet in modernen Browsern eine bedingte Auswahl von Geschwistern. Was CSS jedoch nach wie vor nicht kann, ist die Auswahl nach Textinhalt oder die Navigation entlang einer umgekehrten Achse – und genau diese Textübereinstimmung ist für das Label-Wert-Muster erforderlich. Verwenden Sie CSS für einfache Auswahlen und XPath, wenn Sie Text oder Rückwärtsnavigation benötigen.

Funktioniert „preceding-sibling“ in Selenium und Browsern?

Ja. Browser implementieren XPath 1.0 über document.evaluate, und Selenium nutzt die Engine des Browsers. Die Achse entspricht XPath 1.0 und ist universell verfügbar. Was nicht verfügbar ist, sind alle Funktionen aus XPath 2.0 oder höher – keine regulären Ausdrücke, kein „matches()“, kein „upper-case()“.

Ist „preceding-sibling“ langsam?

Normalerweise nicht. Die Geschwindigkeit hängt von der Anzahl der Geschwisterelemente ab, die in der Regel gering ist. Die „preceding“-Achse ist die langsame, da sie alles weiter vorne im Dokument durchsucht und dies innerhalb einer Schleife quadratisch skaliert. Wenn ein auf Geschwisterelementen basierender Selektor langsam ist, prüfen Sie, ob Sie tatsächlich „preceding“ verwendet haben.

Wie mache ich XPath-Selektoren weniger anfällig?

Verwenden Sie nach Möglichkeit Text statt Positionen als Anker, nutzen Sie „normalize-space()“, um Unterschiede bei Leerzeichen zu überwinden, bevorzugen Sie Bezeichner und Datenattribute gegenüber der Struktur, prüfen Sie vor dem Parsen von HTML auf eingebettetes JSON-LD und validieren Sie die Struktur der extrahierten Daten, damit ein Selektor, der auf das falsche Element trifft, laut statt still fehlschlägt.

Fazit „

preceding-sibling“ ist ein sehr spezifisches Tool mit einer einzigen Aufgabe: rückwärts zwischen Knoten derselben Ebene zu navigieren. Man muss sich vor Augen halten, dass es sich um eine umgekehrte Achse handelt, sodass „[1]“ „nächstgelegen“ statt „erstes“ bedeutet, und das Hinzufügen von Klammern diese Bedeutung komplett umkehrt. Diese eine Unterscheidung ist für die meisten falschen Ergebnisse verantwortlich, die Nutzer damit erhalten.

Sein wahrer Wert liegt im Muster „Bezeichnung-Wert“ – dem Extrahieren eines Feldes, das nur anhand des daneben stehenden Textes identifizierbar ist. CSS hat diese Lücke mit „:has()“ teilweise geschlossen, kann jedoch nach Textinhalt nicht auswählen, und Text ist genau das, was eine Bezeichnung ausmacht. In diesem Fall bleibt XPath das richtige Werkzeug, auch wenn es nicht das gewohnte ist.

Man sollte jedoch bedenken, dass strukturelle Selektoren stillschweigend versagen können. Eine neue Spalte, eine neu geordnete Liste, ein umschließendes div – und schon trifft Ihr Selektor auf etwas anderes, während alles weiterhin einwandfrei aussieht. Verlassen Sie sich, wo immer möglich, auf Text, prüfen Sie vor dem Schreiben eines Selektors auf eingebettete strukturierte Daten und validieren Sie das Ergebnis – denn der Fehler, den Sie sehen können, ist niemals der kostspielige.