如何用XPath获取第二个元素的文本?

24 投票
3 回答
42747 浏览
提问于 2025-04-16 06:42
<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 个回答

2

我不太确定问题出在哪里...

>>> 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']
>>>
2

来自评论:

或者我发的实际HTML简化得太简单了。

你说得对。那么 .//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]
42

我试过这个,但它不管用。

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()

撰写回答