XPath-Abfrage zum Abrufen der n-ten Instanz eines Elements

134

Es gibt eine HTML-Datei (deren Inhalt ich nicht kontrolliere), die mehrere inputElemente mit demselben festen idAttribut von enthält "search_query". Der Inhalt der Datei kann sich ändern, aber ich weiß, dass ich immer das zweite inputElement mit dem ID-Attribut erhalten möchte "search_query".

Ich brauche dazu einen XPath-Ausdruck. Ich habe es versucht, //input[@id="search_query"][2]aber das funktioniert nicht. Hier ist eine XML-Beispielzeichenfolge, bei der diese Abfrage fehlgeschlagen ist:

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

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

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

Beachten Sie, dass das Obige nur ein Beispiel ist und der andere HTML-Code sehr unterschiedlich sein kann und die inputElemente überall ohne konsistente Dokumentstruktur angezeigt werden können (außer dass ich garantiert bin, dass es immer mindestens zwei inputElemente mit dem ID-Attribut von gibt "search_query").

Was ist der richtige XPath-Ausdruck?

rlandster
quelle
Gute Frage, +1. In meiner Antwort finden Sie eine vollständige Erklärung des Problems und die gewünschte Lösung.
Dimitre Novatchev
7
Kleiner Punkt: Sie sollten niemals mehr als ein Element mit einer bestimmten ID haben (und daher ist der HTML-Code in der Frage tatsächlich ungültig). In der Praxis lassen Sie Browser dies trotzdem tun, aber wenn Sie dies tun, verpassen Sie den einzigen Vorteil der Verwendung von IDs, nämlich, dass sie "Ich bin einzigartig" signalisieren (während Klassen für Nicht-IDs konzipiert sind) eindeutige Bezeichner).
Machineghost

Antworten:

244

Dies ist eine FAQ :

//somexpression[$N]

bedeutet "Finde jeden Knoten, der von //somexpressiondiesem ausgewählt wird , ist das $Nth Kind seines Elternteils".

Was Sie wollen ist :

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

Denken Sie daran : Der []Operator hat eine höhere Priorität (Priorität) als die //Abkürzung.

Dimitre Novatchev
quelle
6
Ich mag diese Antwort. Ich hatte kein Vorrangproblem in Betracht gezogen (ich habe nur einen einfachen Vorrang von links nach rechts angenommen).
Rlandster
10
@rlandster: Das Wort "Vorrang" kann verwirrend sein. Die ungekürzte Form von //input[@id='search_query'][2]ist:/descendat-or-self::node()/child::input[attribute::id='search_query'][position()=2]
21
Für diejenigen, die von Google hierher gekommen sind - die Nummerierung beginnt bei 1 - [1] ist das erste Element und so weiter
Jan Mares
Seltsam, dass in diesen XPath-Abfragen diese Art von Arrays mit 1 beginnen, verwirrte mich.
Ivotje50
@ Ivotje50 Ja XPath-Sequenzen und Arrays sind 1-basiert
Dimitre Novatchev
21

Das scheint zu funktionieren:

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

Ich gehe dies aus "XSLT 2.0 und XPath 2.0 Programmer's Reference, 4th Edition" von Michael Kay.

Es gibt auch einen Hinweis im Abschnitt "Abgekürzte Syntax" der XML Path Language-Spezifikation http://www.w3.org/TR/xpath/#path-abbrev , der einen Hinweis lieferte.

rlandster
quelle
Vielen Dank für diese Antwort. In meinem Fall würde die akzeptierte Lösung nicht funktionieren, da ich den xpath im Roboter-Framework verwende, der keine Pfade akzeptiert, die mit Klammern beginnen. Dieser sollte jedoch den Trick tun
dahui