Impara / Cheat sheet
Cheat sheet di XPath e selettori CSS
XPath e CSS affiancati, per Selenium, Playwright e i DevTools del browser. Usa il CSS quando riesce a esprimere ciò che ti serve: è più corto ed è la sintassi che gli sviluppatori conoscono. Passa a XPath quando devi trovare un elemento per testo visibile o risalire l’albero fino a un genitore o a un antenato.
Verificato su XPath 1.0 (quello implementato dai browser), CSS Selectors Level 4 come supportato dalle versioni attuali di Chrome, Firefox e Safari
Basi
| Obiettivo | XPath | CSS |
|---|---|---|
| Elemento per tag (ovunque) | //button | button |
| Per id | //*[@id='email'] | #email |
| Per classe (una tra più classi) | //*[contains(concat(' ', normalize-space(@class), ' '), ' active ')] | .active |
| Figlio diretto | //ul/li | ul > li |
| Qualsiasi discendente | //form//input | form input |
| Più selettori (OR) | //h1 | //h2 | h1, h2 |
| Qualsiasi elemento | //* | * |
Attributi
| Obiettivo | XPath | CSS |
|---|---|---|
| Ha l’attributo | //button[@disabled] | button[disabled] |
| Uguale a | //input[@name='q'] | input[name='q'] |
| Contiene | //a[contains(@href, 'orders')] | a[href*='orders'] |
| Inizia con | //a[starts-with(@href, 'https')] | a[href^='https'] |
| Finisce con | //a[substring(@href, string-length(@href) - 3) = '.pdf'] | a[href$='.pdf'] |
| Due condizioni (AND) | //input[@type='text' and @required] | input[type='text'][required] |
| Una delle due condizioni (OR) | //input[@type='email' or @type='tel'] | input[type='email'], input[type='tel'] |
| Negazione | //input[not(@disabled)] | input:not([disabled]) |
| Test id | //*[@data-testid='submit'] | [data-testid='submit'] |
XPath 1.0 non ha ends-with(): la forma con substring() è l’alternativa portabile.
Testo (solo XPath)
| Obiettivo | XPath | CSS |
|---|---|---|
| Testo esatto, spazi normalizzati | //button[normalize-space()='Sign in'] | non possibile |
| Il testo contiene | //p[contains(., 'expired')] | non possibile |
| Contiene, senza distinzione maiuscole/minuscole | //p[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'expired')] | non possibile |
| Solo il nodo di testo proprio | //label[text()='Email'] | non possibile |
. (il valore stringa) include il testo degli elementi figli; text() trova solo i nodi di testo propri dell’elemento. Preferisci normalize-space() o .
Relazioni e assi
| Obiettivo | XPath | CSS |
|---|---|---|
| Genitore | //input[@id='email']/.. | :has(> #email) |
| Riga antenata di una cella | //td[normalize-space()='#1043']/ancestor::tr | tr:has(td[data-id='1043']) |
| Fratello successivo | //label[.='Password']/following-sibling::input[1] | label + input |
| Tutti i fratelli successivi | //h2/following-sibling::p | h2 ~ p |
| Fratello precedente | //button/preceding-sibling::input[1] | non possibile |
| Cella accanto a una cella etichetta | //td[.='Diego Ramos']/following-sibling::td[1] | non possibile (parte dal testo) |
| Elemento che contiene un figlio | //li[.//button[@aria-label='Remove']] | li:has(button[aria-label='Remove']) |
:has() funziona nelle versioni attuali di Chrome, Edge, Safari e Firefox (121+). I browser più vecchi e alcuni strumenti lo rifiutano.
Posizione
| Obiettivo | XPath | CSS |
|---|---|---|
| Prima riga figlia (base 1) | //tbody/tr[1] | tbody tr:first-child |
| Riga n-esima | //tbody/tr[3] | tbody tr:nth-child(3) |
| Ultima riga | //tbody/tr[last()] | tbody tr:last-child |
| Penultima | //tbody/tr[last()-1] | tbody tr:nth-last-child(2) |
| Tutte le righe pari | //tbody/tr[position() mod 2 = 0] | tbody tr:nth-child(even) |
| Prima corrispondenza nell’intera pagina | (//button)[1] | non possibile: usa first() del tuo tool |
//tr[1] significa "ogni tr che è il primo tr del proprio genitore", non "il primo tr della pagina". Per quello usa (//tr)[1].
Pseudo-classi di stato (CSS)
| Obiettivo | XPath | CSS |
|---|---|---|
| Checkbox o radio selezionato | //input[@checked] (initial state only) | input:checked |
| Disabilitato | //button[@disabled] | button:disabled |
| Campo obbligatorio | //input[@required] | input:required |
| Elemento vuoto | //td[not(node())] | td:empty |
XPath legge gli attributi come scritti nell’HTML, quindi @checked non cambia quando l’utente spunta la casella. Il :checked del CSS segue lo stato reale.
Provali nei DevTools
| Attività | Codice |
|---|---|
| CSS, tutte le corrispondenze (Console) | $$('tbody tr') |
| XPath, tutte le corrispondenze (Console) | $x("//td[.='Mei Lin']") |
| Cerca nel pannello Elements | Ctrl+F / Cmd+F, then type a selector or XPath |
| Conta le corrispondenze | $$('tbody tr').length |