带孩子的XPath文本

时间:2016-12-12 13:27:39

标签: html xpath scrapy

鉴于此html:

<ul>
    <li>This is <a href="#">a link</a></li>
    <li>This is <a href="#">another link</a>.</li>
</ul>

如何使用XPath获得以下结果:

[
    'This is a link',
    'This is another link.'
]

我尝试过的事情:

//ul/li/text()

但这会给我['This is ', 'This is .'](与a代码中的文字一起

此外:

string(//ul/li)

但是这给了我['This is a link'](所以只有第一个元素)

另外

//ul/li/descendant-or-self::text()

但这给了我['This is ', 'a link', 'This is ', 'another link', '.']

还有其他想法吗?

2 个答案:

答案 0 :(得分:3)

XPath通常无法选择不存在的内容。 HTML中不存在这些内容:

[
    'This is a link',
    'This is another link.'
]

它们可能在概念上存在于更高的抽象级别,即浏览器对源代码的呈现,但严格地说,即使它们是分开的,例如在颜色和功能上。

在DOM级别上,只有单独的文本节点,而且所有XPath都可以为您选择。

因此,您有三种选择。

  1. 选择text()个节点,并在Python代码中加入各自的值。
  2. 选择<li>元素,并为每个元素使用Scrapy评估string(.)normalize-space(.)normalize-space()会以你期望的方式处理空格。
  3. 选择<li>元素并访问其.text属性 - 该属性在内部查找所有后代文本节点并为您加入。
  4. 我个人会以//ul/li作为我的基本XPath表达式来使用后者,因为这样可以提供更清晰的解决方案。

    正如@paul在评论中指出的那样,Scrapy提供了一个很好的流畅界面,可以在一行代码中执行多个处理步骤。以下代码实现了变体#2:

    selector = scrapy.Selector(text='''<ul>
        <li>This is <a href="#">a link</a></li>
        <li>This is <a href="#">another link</a>.</li>
    </ul>''')
    
    selector.css('ul > li').xpath('normalize-space()').extract()
    # --> [u'This is a link', u'This is another link.']
    

答案 1 :(得分:2)

@Tomalak在saying中是正确的,XPath通常无法选择那些不存在的内容。

但是,在这种情况下,您需要的结果是listring(//ul/li) 元素。正如您所发现的那样,

//ul/li

让你关闭,但只返回第一个所需的字符串。

这指出了XPath 2.0中解决的XPath 1.0的缺点。

XPath 1.0 中,您必须在XPath之外迭代由//ul/li/string() 选择的节点集 - 在XSLT,Python,Java等中。

XPath 2.0 中,最后一个位置步骤可以是一个函数,因此您可以使用,

This is a link
This is another link.

直接返回

string()

按要求。

如果您坚持使用仅支持XPath 1.0但又知道

的Scrapy,这比实际更具教育意义
  • XPath 1.0只将第一个节点集传递给private val converters = HashMap<String, ValueConverter<Event>>()
  • XPath 2.0允许最后一个位置步骤为函数,
  • string values

通常有助于推理XPath文本选择。