Abbiamo visto sinora come sia possibile utilizzare Selenium per navigare l'albero degli oggetti all'interno di una pagina web. Ciò è fondamentale per poter indicare quale elemento della pagina dovrà essere trasformato in un oggetto Python e potervi interagire. Sfruttando il sistema di id, classi e tag abbiamo visto che c'è la possibilità di selezionare elementi in maniera chirurgica ma, ci potremmo chiedere: trattandosi di un albero ovvero una serie di sentieri all'interno del documento, non ci sarebbe un modo per descrivere in un'unica espressione qualcosa come "prendi la terza riga della tabella con un certo id e seleziona il suo quarto campo"?
Sì esiste ed è uno strumento ideale per tracciare percorsi, path, all'interno di un documento: si chiama XPath ed in questo articolo iniziamo a conoscerlo.
Cos'è XPath?
Si tratta di un formalismo che permette di descrivere in una sola espressione il percorso che ci porterà certamente ad uno specifico elemento o ad una sequenza di elementi. XPath, tra l'altro, può essere studiato senza grandi difficoltà facendo così un ottimo investimento. È utilizzato infatti da molte altre tecnologie e linguaggi ed adattabile non solo a HTML ma anche ad altre strutture a tag come XML. Anzi potremmo forse attribuire proprio a XML e al web semantico, la diffusione di XPath e alla sua capacità di filtrare dati stratificati tra tag personalizzati.
Iniziamo a conoscerne la sintassi.
Sintassi delle espressioni XPath
La sintassi XPath contempla molti elementi tra cui anche operatori. Noi ci concentreremo maggiormente sugli elementi utili nell'utilizzo di Selenium. Il flusso di lavoro tipico con Selenium e XPath consiste nei seguenti punti:
- definire la query XPath che permette di indirizzare uno o più elementi della pagina di nostro interesse. Si tratterà essenzialmente di una stringa di cui comprenderemo a breve
gli elementi sintattici; - eseguire una ricerca mediante i metodi
find_element
(per un elemento singolo) ofind_elements
(per una sequenza di elementi). A differenza dei casi visti in precedenza non cercheremo per nome o tag bensì per query XPath nel seguente modo:
driver.find_element(By.XPATH,....)
- utilizzare gli elementi rilevati come normali oggetti Selenium interagendovi secondo necessità.
Prima di andare avanti si ricordi un aspetto molto utile. Quando si sta prendendo dimestichezza con XPath ci si trova a volte ad ottenere risultati non confacenti alle nostre aspettative, magari racchiusi all'interno di un oggetto di classe WebElement
. Dovesse capitare, in fase di debug può essere utile utilizzare .get_attribute('innerHTML')
sull'elemento ottenuto e questo mostrerà tutto il suo HTML interno illustrandoci eventuali errori "di percorso" in cui potremmo essere incappati.
Sintassi delle espressioni XPath
Una query XPath inizia con un doppio slash e prosegue con una serie di segmenti separati a loro volta da slash singoli. Ci occupiamo subito di imparare come individuare elementi in base al tag, all'id, alla classe e alla loro posizione in una sequenza.
Prendiamo il seguente HTML:
<p id="descrizione">paragrafo 1</p>
<p>paragrafo 2</p>
<p class="definizione">paragrafo 3</p>
<p class="definizione">paragrafo 4</p>
Abbiamo quattro paragrafi di cui uno con attributo id
, due con class
nonché uno vuoto. Possiamo richiedere tutti i paragrafi cercando gli elementi che hanno il tag p
:
//p
quello con id
uguale a "descrizione":
//p[@id='descrizione']
oppure quelli con l'attributo class
impostato a "definizione":
//p[@class='definizione']
Parentesi quadre e chiocciola ci hanno permesso di specificare quali elementi vogliamo includere nel risultato. Possiamo anche richiedere elementi in base alla posizione ma indicando un numero tra parentesi quadre che indichi quale elemento desideriamo. Supponiamo di lavorare con una tabella, la seguente:
<table>
<tr>
<td>Rossi</td>
<td>Matteo</td>
<td>16</td>
</tr>
<tr>
<td>Bianchi</td>
<td>Elena</td>
<td>28</td>
</tr>
<tr>
<td>Azzurri</td>
<td>Sonia</td>
<td>25</td>
</tr>
<tr>
<td>Verdi</td>
<td>Ivan</td>
<td>19</td>
</tr>
</table>
La stringa Elena si trova nella seconda riga (tag tr
) nella seconda cella (tag td
). Possiamo richiederla con la seguente espressione XPath:
//table/tr[2]/td[2]
dove le posizioni vengono contate a partire da 1. Potremmo richiedere altrimenti il valore 19 ovvero l'ultima cella dell'ultima riga con un percorso completo:
//table/tr[last()]/td[last()]
mentre con //td[last()
otterremo tutti i valori della colonna più a destra, in pratica la collezione di tutte le ultime celle di ogni riga.
Un elemento molto interessante è last()
che permette di fare riferimento all'ultimo elemento di una sequenza e può essere declinato con espressioni come last()-1
per intendere il penultimo e via dicendo.
Altrettanto interessante è l'asterisco utilizzabile per dire "qualsiasi elemento" e vi si possono applicare i vari selettori tra parentesi quadre visti in precedenza come
//*[@class='nome']
con cui individuiamo qualsiasi tag di classe "nome".
Tutto qui?
La nostra carrellata di elementi di XPath potrebbe proseguire ma ci fermiamo qui perché abbiamo raccolto gli elementi utili per l'uso che ne dobbiamo fare in Selenium con Python.
Nella prossima lezione proseguiremo con esempi pratici mentre XPath potrà essere ulteriormente approfondito sulla documentazione ufficiale.