Lernen / Cheat Sheet
Cheat Sheet zu XPath- und CSS-Selektoren
XPath und CSS nebeneinander, für Selenium, Playwright und die Browser-DevTools. Nehmen Sie CSS, wenn es ausdrücken kann, was Sie brauchen – es ist kürzer und die Syntax kennen Entwickler bereits. Greifen Sie zu XPath, wenn Sie sichtbaren Text abgleichen oder im DOM-Baum nach oben zu einem Eltern- oder Vorfahrenelement wandern müssen.
Geprüft mit XPath 1.0 (wie in Browsern implementiert), CSS Selectors Level 4 in der Unterstützung aktueller Versionen von Chrome, Firefox und Safari
Grundlagen
| Ziel | XPath | CSS |
|---|---|---|
| Element nach Tag (überall) | //button | button |
| Nach ID | //*[@id='email'] | #email |
| Nach Klasse (eine von mehreren) | //*[contains(concat(' ', normalize-space(@class), ' '), ' active ')] | .active |
| Direktes Kindelement | //ul/li | ul > li |
| Beliebiger Nachfahre | //form//input | form input |
| Mehrere Selektoren (ODER) | //h1 | //h2 | h1, h2 |
| Beliebiges Element | //* | * |
Attribute
| Ziel | XPath | CSS |
|---|---|---|
| Hat Attribut | //button[@disabled] | button[disabled] |
| Gleich | //input[@name='q'] | input[name='q'] |
| Enthält | //a[contains(@href, 'orders')] | a[href*='orders'] |
| Beginnt mit | //a[starts-with(@href, 'https')] | a[href^='https'] |
| Endet mit | //a[substring(@href, string-length(@href) - 3) = '.pdf'] | a[href$='.pdf'] |
| Zwei Bedingungen (UND) | //input[@type='text' and @required] | input[type='text'][required] |
| Eine der Bedingungen (ODER) | //input[@type='email' or @type='tel'] | input[type='email'], input[type='tel'] |
| Nicht | //input[not(@disabled)] | input:not([disabled]) |
| Test-ID | //*[@data-testid='submit'] | [data-testid='submit'] |
XPath 1.0 kennt kein ends-with(): Die substring()-Variante ist der portable Workaround.
Text (nur XPath)
| Ziel | XPath | CSS |
|---|---|---|
| Exakter Text, Leerraum normalisiert | //button[normalize-space()='Sign in'] | nicht möglich |
| Text enthält | //p[contains(., 'expired')] | nicht möglich |
| Enthält, ohne Groß-/Kleinschreibung | //p[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'expired')] | nicht möglich |
| Nur der eigene Textknoten | //label[text()='Email'] | nicht möglich |
. (der String-Wert) schließt Text in Kindelementen ein; text() trifft nur die eigenen Textknoten des Elements. Bevorzugen Sie normalize-space() oder .
Beziehungen und Achsen
| Ziel | XPath | CSS |
|---|---|---|
| Elternelement | //input[@id='email']/.. | :has(> #email) |
| Zeile (Vorfahre) einer Zelle | //td[normalize-space()='#1043']/ancestor::tr | tr:has(td[data-id='1043']) |
| Nächstes Geschwisterelement | //label[.='Password']/following-sibling::input[1] | label + input |
| Alle folgenden Geschwister | //h2/following-sibling::p | h2 ~ p |
| Vorheriges Geschwisterelement | //button/preceding-sibling::input[1] | nicht möglich |
| Zelle neben einer Label-Zelle | //td[.='Diego Ramos']/following-sibling::td[1] | nicht möglich (geht vom Text aus) |
| Element mit bestimmtem Kindelement | //li[.//button[@aria-label='Remove']] | li:has(button[aria-label='Remove']) |
:has() funktioniert in aktuellen Versionen von Chrome, Edge, Safari und Firefox (ab 121). Ältere Browser und manche Tools lehnen es ab.
Position
| Ziel | XPath | CSS |
|---|---|---|
| Erste Kindzeile (1-basiert) | //tbody/tr[1] | tbody tr:first-child |
| n-te Zeile | //tbody/tr[3] | tbody tr:nth-child(3) |
| Letzte Zeile | //tbody/tr[last()] | tbody tr:last-child |
| Vorletzte | //tbody/tr[last()-1] | tbody tr:nth-last-child(2) |
| Jede gerade Zeile | //tbody/tr[position() mod 2 = 0] | tbody tr:nth-child(even) |
| Erster Treffer auf der ganzen Seite | (//button)[1] | nicht möglich: first() Ihres Tools verwenden |
//tr[1] bedeutet „jedes tr, das das erste tr in seinem Elternelement ist“, nicht „das erste tr auf der Seite“. Dafür verwenden Sie (//tr)[1].
Zustands-Pseudoklassen (CSS)
| Ziel | XPath | CSS |
|---|---|---|
| Angehakte Checkbox oder Radiobutton | //input[@checked] (initial state only) | input:checked |
| Deaktiviert | //button[@disabled] | button:disabled |
| Pflichtfeld | //input[@required] | input:required |
| Leeres Element | //td[not(node())] | td:empty |
XPath liest Attribute so, wie sie im HTML stehen – @checked ändert sich also nicht, wenn ein Nutzer die Checkbox anhakt. CSS :checked folgt dem Live-Zustand.
In den DevTools ausprobieren
| Aufgabe | Code |
|---|---|
| CSS, alle Treffer (Console) | $$('tbody tr') |
| XPath, alle Treffer (Console) | $x("//td[.='Mei Lin']") |
| Im Elements-Panel suchen | Ctrl+F / Cmd+F, then type a selector or XPath |
| Treffer zählen | $$('tbody tr').length |