Requête XPath pour obtenir la nième instance d'un élément

135

Il existe un fichier HTML (dont je ne contrôle pas le contenu) qui contient plusieurs inputéléments tous avec le même idattribut fixe de "search_query". Le contenu du fichier peut changer, mais je sais que je veux toujours obtenir le deuxième inputélément avec l'attribut id "search_query".

J'ai besoin d'une expression XPath pour ce faire. J'ai essayé //input[@id="search_query"][2]mais ça ne marche pas. Voici un exemple de chaîne XML où cette requête a échoué:

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

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

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

Gardez à l'esprit que ce qui précède n'est qu'un exemple et que l'autre code HTML peut être très différent et que les inputéléments peuvent apparaître n'importe où sans structure de document cohérente (sauf que je suis assuré qu'il y aura toujours au moins deux inputéléments avec un attribut id de "search_query").

Quelle est l'expression XPath correcte?

rlandster
la source
Bonne question, +1. Voir ma réponse pour une explication complète du problème et pour la solution recherchée.
Dimitre Novatchev
7
Point mineur: vous ne devriez jamais avoir plus d'un élément avec un ID donné (et donc le HTML de la question est en fait invalide). En pratique, les navigateurs vous permettront de le faire de toute façon, mais si vous le faites, vous passez à côté du seul avantage de l'utilisation des identifiants, qui est qu'ils signalent "Je suis unique" (alors que les classes sont conçues pour être utilisées pour des signifiants uniques).
machineghost

Réponses:

244

Ceci est une FAQ :

//somexpression[$N]

signifie "Trouver chaque nœud sélectionné par //somexpressionqui est le $Nième enfant de son parent".

Ce que tu veux c'est :

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

N'oubliez pas : l' []opérateur a une priorité (priorité) plus élevée que l' //abréviation.

Dimitre Novatchev
la source
6
J'aime cette réponse. Je n'avais pas envisagé un problème de priorité (j'ai simplement supposé une simple priorité de gauche à droite).
rlandster
10
@rlandster: Le mot "précédence" peut prêter à confusion. La forme non abrégée de //input[@id='search_query'][2]est:/descendat-or-self::node()/child::input[attribute::id='search_query'][position()=2]
21
Pour ceux qui sont arrivés ici de Google - la numérotation commence à partir de 1 - [1] étant le premier élément et ainsi de suite
Jan Mares
Bizarre que dans ces requêtes XPath, ces types de tableaux commencent par 1, m'a confus.
Ivotje50
@ Ivotje50 Oui Les séquences et tableaux XPath sont basés sur 1
Dimitre Novatchev
21

Cela semble fonctionner:

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

Je vais cela de "XSLT 2.0 et XPath 2.0 Programmer's Reference, 4th Edition" par Michael Kay.

Il y a aussi une note dans la section "Syntaxe abrégée" de la spécification XML Path Language http://www.w3.org/TR/xpath/#path-abbrev qui a fourni un indice.

rlandster
la source
Merci beaucoup pour cette réponse. Dans mon cas, la solution acceptée ne fonctionnerait pas car j'utilise le xpath dans le cadre du robot, qui n'accepterait pas les chemins commençant par des crochets. Celui-ci devrait cependant faire l'affaire
dahui