XPath-запрос для получения n-го экземпляра элемента


135

Существует HTML-файл (содержимое которого я не контролирую), который имеет несколько inputэлементов с одинаковым фиксированным idатрибутом "search_query". Содержимое файла может измениться, но я знаю, что всегда хочу получить второй inputэлемент с атрибутом id "search_query".

Мне нужно выражение XPath, чтобы сделать это. Я пытался, //input[@id="search_query"][2]но это не работает. Вот пример строки XML, где этот запрос не удался:

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

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

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

Имейте в виду, что вышеприведенное является просто примером, а другой HTML-код может сильно отличаться, а inputэлементы могут появляться в любом месте без согласованной структуры документа (за исключением того, что я гарантирую, что всегда будет хотя бы два inputэлемента с атрибутом id, равным "search_query").

Какое правильное выражение XPath?


Хороший вопрос, +1. Смотрите мой ответ для полного объяснения проблемы и для поиска решения.
Димитр Новатчев

7
Незначительный момент: у вас никогда не должно быть более одного элемента с заданным идентификатором (и поэтому HTML в вопросе на самом деле недопустим) На практике браузеры в любом случае позволят вам это сделать, но если вы это сделаете, вы упускаете единственное преимущество использования идентификаторов, а именно то, что они сигнализируют «я уникален» (тогда как классы предназначены для использования для уникальные обозначения).
machineghost

Ответы:


244

Это часто задаваемые вопросы :

//somexpression[$N]

означает «Найти каждый узел, выбранный тем, //somexpressionкоторый является $Nпотомком его родителя».

То, что вы хотите, это :

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

Помните : []оператор имеет более высокий приоритет (приоритет), чем //сокращение.


6
Мне нравится этот ответ. Я не рассматривал проблему приоритета (я просто предполагал простой приоритет слева направо).
Rlandster

10
@rlandster: слово «приоритет» может сбивать с толку. Форма без //input[@id='search_query'][2]/descendat-or-self::node()/child::input[attribute::id='search_query'][position()=2]

21
Для тех, кто попал сюда из Google - нумерация начинается с 1 - [1] - первый элемент и т. Д.
Ян Марес

Странно, что в этих XPath-запросах такие массивы начинаются с 1, меня смутило.
Ivotje50

@ Ivotje50 Да Последовательности и массивы XPath основаны на 1
Dimitre Novatchev

21

Это похоже на работу:

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

Я беру это из «Справочника по программированию XSLT 2.0 и XPath 2.0, 4-е издание» Майкла Кея.

Существует также примечание в разделе «Сокращенный синтаксис» спецификации языка путей XML http://www.w3.org/TR/xpath/#path-abbrev, в котором содержится подсказка.


Большое спасибо за этот ответ. В моем случае принятое решение не будет работать, так как я использую xpath в среде робота, которая не будет принимать пути, начинающиеся с скобок. Этот, однако, должен сделать трюк
Дахуэй
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.