XPath text() 没有获取 link 节点的文本

XPath text() does not get the text of a link node

from lxml import etree
import requests
htmlparser = etree.HTMLParser()
f = requests.get('https://rss.orf.at/news.xml')
# without the ufeff this would fail because it tells me: "ValueError: Unicode strings with encoding declaration are not supported. Please use bytes input or XML fragments without declaration."
tree = etree.fromstring('\ufeff'+f.text, htmlparser)
print(tree.xpath('//item/title/text()')) #<- this does produce a liste of titles  
print(tree.xpath('//item/link/text()')) #<- this does NOT produce a liste of links why ?!?!

好吧,这对我来说有点神秘,也许我只是忽略了最简单的事情,但是 XPath '//item/link/text()' 确实只生成一个空列表,而 '//item/title/text()' 完全按照预期工作. <link> 节点是否有任何特殊用途?我可以 select 所有这些 '//item/link' 我只是无法获得 text() select 或处理它们。

您正在使用 etree.HTMLParser 解析 XML 文档。我怀疑这是处理 XML 命名空间的尝试,但我认为这可能是错误的解决方案。可能将 XML 文档视为 HTML 最终是问题的根源。

如果我们改用 XML 解析器,一切都会按预期工作。

首先,如果我们查看根元素,我们会看到它设置了一个默认命名空间:

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:orfon="http://rss.orf.at/1.0/"
  xmlns="http://purl.org/rss/1.0/"
>

这意味着当我们在文档中看到一个 item 元素时,它实际上是一个“item in the http://purl.org/rss/1.0/ namespace”元素。我们需要通过传入 namespaces 字典并在元素名称上使用命名空间前缀来在我们的 xpath 查询中提供该命名空间信息,如下所示:

>>> tree.xpath('//rss:item', namespaces={'rss': 'http://purl.org/rss/1.0/'})
[<Element {http://purl.org/rss/1.0/}item at 0x7f0497000e80>, ...]

您的第一个 xpath 表达式(查看 /item/title/text())变为:

>>> tree.xpath('//rss:item/rss:title/text()', namespaces={'rss': 'http://purl.org/rss/1.0/'})
['Amnesty dokumentiert Kriegsverbrechen', ..., 'Moskauer Börse startet abgeschirmten Handel']

你的第二个 xpath 表达式(查看 /item/link/text())变为:

>>> tree.xpath('//rss:item/rss:link/text()', namespaces={'rss': 'http://purl.org/rss/1.0/'})
['https://orf.at/stories/3255477/', ..., 'https://orf.at/stories/3255384/']

这使得代码看起来像:

from lxml import etree
import requests
f = requests.get('https://rss.orf.at/news.xml')
tree = etree.fromstring(f.content)
print(tree.xpath('//rss:item/rss:title/text()', namespaces={'rss': 'http://purl.org/rss/1.0/'}))
print(tree.xpath('//rss:item/rss:link/text()', namespaces={'rss': 'http://purl.org/rss/1.0/'}))

请注意,通过使用 f.content(字节字符串)而不是 f.text(unicode 字符串),我们避免了整个 unicode 解析错误。