Получить второй элемент текста с XPath? - PullRequest
20 голосов
/ 07 ноября 2010
<span class='python'>
  <a>google</a>
  <a>chrome</a>
</span>

Я хочу получить chrome и сделать так, чтобы это уже работало.

q = item.findall('.//span[@class="python"]//a')
t = q[1].text # first element = 0

Я хотел бы объединить его в одно выражение XPath и просто получить один элемент вместо списка.
Я пробовал это, но это не работает.

t = item.findtext('.//span[@class="python"]//a[2]') # first element = 1

И настоящий, а не упрощенный, HTML выглядит следующим образом.

<span class='python'>
  <span>
    <span>
      <img></img>
      <a>google</a>
    </span>
    <a>chrome</a>
  </span>
</span>

Ответы [ 3 ]

35 голосов
/ 07 ноября 2010

Я пробовал это, но это не работает.

t = item.findtext('.//span[@class="python"]//a[2]')

Это часто задаваемые вопросы о // сокращении .

.//a[2] означает: Выбрать все a потомков текущего узла, которые являются вторым a дочерним элементом их родителя.Таким образом, можно выбрать более одного элемента или ни одного элемента - в зависимости от конкретного документа XML.

Проще говоря, оператор [] имеет более высокий приоритет, чем //.

Если вы хотите вернуть только один (второй) из всех узлов, вы должны использовать скобки, чтобы установить желаемый приоритет:

(.//a)[2]

Это действительно выбирает второго a потомкатекущий узел.

Для фактического выражения, используемого в вопросе, измените его на :

(.//span[@class="python"]//a)[2]

или измените его на:

(.//span[@class="python"]//a)[2]/text()
2 голосов
/ 07 ноября 2010

Из комментариев:

или упрощенный текст, который я разместил, слишком прост

Вы правы.Что означает .//span[@class="python"]//a[2]?Это будет расширено до:

self::node()
 /descendant-or-self::node()
  /child::span[attribute::class="python"]
   /descendant-or-self::node()
    /child::a[position()=2]

Окончательно выберется второй a дочерний элемент (fn:position() относится к child топору).Таким образом, ничего не будет выбрано, если ваш документ будет выглядеть так:

<span class='python'> 
  <span> 
    <span> 
      <img></img> 
      <a>google</a><!-- This is the first "a" child of its parent --> 
    </span> 
    <a>chrome</a><!-- This is also the first "a" child of its parent --> 
  </span> 
</span> 

Если вы хотите второго из всех потомков, используйте:

descendant::span[@class="python"]/descendant::a[2]
2 голосов
/ 07 ноября 2010

Я не уверен, в чем проблема ...

>>> d = """<span class='python'>
...   <a>google</a>
...   <a>chrome</a>
... </span>"""
>>> from lxml import etree
>>> d = etree.HTML(d)
>>> d.xpath('.//span[@class="python"]/a[2]/text()')
['chrome']
>>>
...