Consulta XPath para obter a enésima instância de um elemento

135

Existe um arquivo HTML (cujo conteúdo eu não controlo) que possui vários inputelementos, todos com o mesmo idatributo fixo de "search_query". O conteúdo do arquivo pode mudar, mas eu sei que sempre quero obter o segundo inputelemento com o atributo id "search_query".

Eu preciso de uma expressão XPath para fazer isso. Eu tentei, //input[@id="search_query"][2]mas isso não funciona. Aqui está um exemplo de string XML em que esta consulta falhou:

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

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

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

Lembre-se de que o exposto acima é apenas um exemplo e o outro código HTML pode ser bem diferente e os inputelementos podem aparecer em qualquer lugar sem uma estrutura de documento consistente (exceto que eu estou garantido que sempre haverá pelo menos dois inputelementos com o atributo id "search_query")

Qual é a expressão XPath correta?

rlandster
fonte
Boa pergunta, +1. Veja minha resposta para uma explicação completa do problema e para a solução desejada.
Dimitre Novatchev 24/10/10
7
Ponto secundário: você nunca deve ter mais de um elemento com um determinado ID (e, portanto, o HTML na pergunta é realmente inválido). Na prática, os navegadores permitirão que você faça isso de qualquer maneira, mas, se o fizer, estará perdendo o único benefício de usar IDs, ou seja, eles sinalizam "Sou único" (enquanto as classes são projetadas para serem usadas para não- significantes únicos).
precisa saber é o seguinte

Respostas:

244

Esta é uma FAQ :

//somexpression[$N]

significa "Encontre todos os nós selecionados por //somexpressionesse é o $Nth filho de seu pai".

O que você quer é :

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

Lembre - se : O []operador tem maior precedência (prioridade) que a //abreviação.

Dimitre Novatchev
fonte
6
Eu gosto desta resposta. Eu não havia considerado um problema de precedência (apenas assumi uma precedência simples da esquerda para a direita).
Rlandster 24/10/10
10
@landland: A palavra "precedência" pode ser confusa. A forma não abreviada de //input[@id='search_query'][2]é:/descendat-or-self::node()/child::input[attribute::id='search_query'][position()=2]
21
Para quem chegou aqui do Google - a numeração começa em 1 - [1] sendo o primeiro elemento e assim por diante
Jan Mares
Estranho que nessas consultas XPath esses tipos de matrizes comecem com 1, me confundiu.
Ivotje50
@ Ivotje50 Sim Seqüências e matrizes XPath são baseadas em 1
Dimitre Novatchev 15/09/19
21

Isso parece funcionar:

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

Eu digo isso em "Referência do programador XSLT 2.0 e XPath 2.0, 4ª edição", de Michael Kay.

Há também uma observação na seção "Sintaxe abreviada" da especificação XML Path Language http://www.w3.org/TR/xpath/#path-abbrev que forneceu uma pista.

rlandster
fonte
Muito obrigado por esta resposta. No meu caso, a solução aceita não funcionaria, pois estou usando o xpath na estrutura do robô, que não aceita caminhos que começam com colchetes. Este no entanto, deve fazer o truque
Dahui