如何通过lxml扩展函数模拟XPath 2.0函数?
我正在参考lxml的扩展函数文档,想要模仿upper-case
这个XPath 2.0中的函数。
import urllib
from lxml import html, etree
ns = etree.FunctionNamespace(None)
ns['upper-case'] = lambda context, s: str.upper(s)
google_page = urllib.request.urlopen('http://www.google.com').read().decode('latin-1')
google_page_tree = html.fromstring(google_page)
# text == ['Google.com']
text = google_page_tree.xpath('//a[@id="fehl"]/text()')
# TypeError: descriptor 'upper' requires a 'str' object but received a 'list'
text = google_page_tree.xpath('//a[upper-case(@id)="FEHL"]/text()')
看起来这样做不太对,因为我发现upper-case
接收到的是一个空列表[]
。有什么想法吗?谢谢。
2 个回答
1
你不需要去模仿 xpath 2.0 的功能。其实你可以通过使用 elementpath
来在 lxml 中使用这些功能。
如果你的 Python 版本是 3.6 或更高,可以通过 pip 来安装它。
pip install elementpath
然后,导入 elementpath 和 lxml 这两个库。
import elementpath
from lxml import etree
root = etree.XML("<book name='sense and sensibility'/>")
elementpath.select(root, "upper-case(@name)")
这样做会产生:
'SENSE AND SENSIBILITY'
如果你的 xpath 返回的是节点,那么返回的类型就是一个 lxml.etree._Element
的列表。所以你可以把 elementpath
看作是 lxml
的一个扩展。
参考资料:
3
我对你的XPath API不太熟悉,但在XPath 1.0中,@id
可以选择一个包含单个属性节点的节点集合,而在XPath 2.0中,它则选择一个包含单个属性节点的序列。我猜str.upper
这个方法是需要一个字符串值的,所以你可以试试用//a[upper-case(string(@id)) = ...]
,而不是//a[upper-case(@id) = ...]
。这样,XPath表达式就会返回一个字符串,Python函数就能正确处理这个字符串了。