Query XPath per ottenere l'ennesima istanza di un elemento


135

Esiste un file HTML (i cui contenuti non controllo) con diversi inputelementi tutti con lo stesso idattributo fisso di "search_query". Il contenuto del file può cambiare, ma so che voglio sempre ottenere il secondo inputelemento con l'attributo id "search_query".

Ho bisogno di un'espressione XPath per fare questo. Ci ho provato //input[@id="search_query"][2]ma non funziona. Ecco una stringa XML di esempio in cui questa query non è riuscita:

<div>
  <form>
    <input id="search_query" />
   </form>
</div>

<div>
  <form>
    <input id="search_query" />
  </form>
</div>

<div>
  <form>
    <input id="search_query" />
  </form>
</div>

Tieni presente che quanto sopra è solo un esempio e l'altro codice HTML può essere abbastanza diverso e gli inputelementi possono apparire ovunque senza una struttura di documento coerente (tranne che sono garantito che ci saranno sempre almeno due inputelementi con un attributo id di "search_query").

Qual è l'espressione XPath corretta?


Buona domanda, +1. Vedi la mia risposta per una spiegazione completa del problema e per la soluzione desiderata.
Dimitre Novatchev,

7
Punto minore: non dovresti mai avere più di un elemento con un determinato ID (e quindi l'HTML nella domanda non è effettivamente valido). In pratica, i browser ti permetteranno di farlo comunque, ma se lo fai ti perdi l'unico vantaggio dell'uso degli ID, che è che segnalano "Sono unico" (mentre le classi sono progettate per essere utilizzate per significanti unici).
machineghost,

Risposte:


244

Questa è una FAQ :

//somexpression[$N]

significa "Trova ogni nodo selezionato da //somexpressionquello è il $Nfiglio del suo genitore".

Quello che vuoi è :

(//input[@id="search_query"])[2]

Ricorda : l' []operatore ha una precedenza (priorità) più alta //dell'abbreviazione.


6
Mi piace questa risposta. Non avevo considerato un problema di precedenza (ho semplicemente assunto la precedenza da sinistra a destra).
rlandster,

10
@rlandster: la parola "precedenza" può essere fonte di confusione. La forma non alterata di //input[@id='search_query'][2]è:/descendat-or-self::node()/child::input[attribute::id='search_query'][position()=2]

21
Per coloro che sono arrivati ​​qui da Google - la numerazione inizia da 1 - [1] essendo il primo elemento e così via
Jan Mares,

Strano che in queste query XPath questo tipo di array inizi con 1, mi ha confuso.
Ivotje50,

@ Ivotje50 Sì Le sequenze e le matrici XPath sono basate su 1
Dimitre Novatchev

21

Questo sembra funzionare:

/descendant::input[@id="search_query"][2]

Vado da "XSLT 2.0 e XPath 2.0 Programmer's Reference, 4th Edition" di Michael Kay.

C'è anche una nota nella sezione "Sintassi abbreviata" della specifica XML Path Language http://www.w3.org/TR/xpath/#path-abbrev che ha fornito un indizio.


Mille grazie per questa risposta. Nel mio caso la soluzione accettata non funzionerebbe poiché sto usando xpath nel framework robot, che non accetterebbe percorsi che iniziano con parentesi. Questo, tuttavia, dovrebbe fare il trucco
dahui,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.