Consulta XPath para obtener la enésima instancia de un elemento

135

Hay un archivo HTML (cuyo contenido no controlo) que tiene varios inputelementos, todos con el mismo idatributo fijo de "search_query". El contenido del archivo puede cambiar, pero sé que siempre quiero obtener el segundo inputelemento con el atributo id "search_query".

Necesito una expresión XPath para hacer esto. Lo intenté //input[@id="search_query"][2]pero eso no funciona. Aquí hay una cadena XML de ejemplo donde esta consulta falló:

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

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

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

Tenga en cuenta que lo anterior es simplemente un ejemplo y que el otro código HTML puede ser bastante diferente y que los inputelementos pueden aparecer en cualquier lugar sin una estructura de documento coherente (excepto que estoy seguro de que siempre habrá al menos dos inputelementos con un atributo id de "search_query")

¿Cuál es la expresión correcta de XPath?

rlandster
fuente
Buena pregunta, +1. Consulte mi respuesta para obtener una explicación completa del problema y la solución deseada.
Dimitre Novatchev
77
Punto menor: nunca debe tener más de un elemento con una ID dada (y, por lo tanto, el HTML en la pregunta no es realmente válido). En la práctica, los navegadores le permitirán hacerlo de todos modos, pero si lo hace, se está perdiendo el único beneficio de usar ID, que es que indican "Soy único" (mientras que las clases están diseñadas para usarse para significantes únicos).
machineghost

Respuestas:

244

Esta es una pregunta frecuente :

//somexpression[$N]

significa "Buscar cada nodo seleccionado por //somexpressionese es el $Nhijo de su padre".

Lo que quieres es :

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

Recuerde : el []operador tiene mayor prioridad (prioridad) que la //abreviatura.

Dimitre Novatchev
fuente
66
Me gusta esta respuesta No había considerado un problema de precedencia (simplemente asumí una precedencia simple de izquierda a derecha).
rlandster
10
@rlandster: La palabra "precedencia" puede ser confusa. La forma no abreviada de //input[@id='search_query'][2]es:/descendat-or-self::node()/child::input[attribute::id='search_query'][position()=2]
21
Para aquellos que llegaron aquí desde Google, la numeración comienza desde 1 - [1] es el primer elemento, etc.
Jan Mares
Extraño que en estas consultas XPath este tipo de matrices comience con 1, me confundió.
Ivotje50
@ Ivotje50 Sí Las secuencias y matrices XPath están basadas en 1
Dimitre Novatchev
21

Esto parece funcionar:

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

Voy esto de "XSLT 2.0 y XPath 2.0 Programmer's Reference, 4th Edition" de Michael Kay.

También hay una nota en la sección "Sintaxis abreviada" de la especificación XML Path Language http://www.w3.org/TR/xpath/#path-abbrev que proporciona una pista.

rlandster
fuente
Muchas gracias por esta respuesta. En mi caso, la solución aceptada no funcionaría ya que estoy usando el xpath en el marco del robot, que no aceptaría rutas que comiencen entre paréntesis. Sin embargo, este debería ser el truco
dahui