在带有LXML的Python中使用XPath

2022-04-01 00:00:00 python xml xpath contains lxml

问题描述

我有一个用来解析XML并将某些感兴趣的元素导出到CSV文件中的Python脚本。现在,我尝试更改脚本以允许根据条件过滤XML文件,等价的XPath查询将为:

DCEventsConfirmation[contains(TransactionId,"GTEREVIEW")]

当我尝试使用lxml执行此操作时,我的代码是:

xml_file = lxml.etree.parse(xml_file_path)
namespace = "{" + xml_file.getroot().nsmap[None] + "}"
node_list = xml_file.findall(namespace + "Events/" + namespace + "Confirmation[TransactionId='*GTEREVIEW*']")

但这似乎不起作用。有人能帮忙吗? XML文件示例:

<Events>
  <Confirmation>
    <TransactionId>GTEREVIEW2012</TransactionId>
  </Confirmation>    
  <Confirmation>
    <TransactionId>GTEDEF2012</TransactionId>
  </Confirmation>    
</Events> 

因此,我希望所有"确认"节点都包含包含字符串"GTEREVIEW"的事务ID。 谢谢


解决方案

findall()不支持XPath表达式,只支持ElementPath(请参阅https://web.archive.org/web/20200504162744/http://effbot.org/zone/element-xpath.htm)。ElementPath不支持搜索包含特定字符串的元素。

为什么不使用XPath?假设文件test.xml包含您的示例XML,则可以执行以下操作:

> python
Python 2.7.9 (default, Jun 29 2016, 13:08:31) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> from lxml import etree
>>> tree=etree.parse("test.xml")
>>> tree.xpath("Confirmation[starts-with(TransactionId, 'GTEREVIEW')]")
[<Element Confirmation at 0x7f68b16c3c20>]

如果您坚持使用findall(),则最多只能获取具有TransactionId子节点的所有Confirmation元素的列表:

>>> tree.findall("Confirmation[TransactionId]")
[<Element Confirmation at 0x7f68b16c3c20>, <Element Confirmation at 0x7f68b16c3ea8>]

然后您需要手动筛选此列表,例如:

>>> [e for e in tree.findall("Confirmation[TransactionId]")
     if e[0].text.startswith('GTEREVIEW')]
[<Element Confirmation at 0x7f68b16c3c20>]

如果您的文档包含命名空间,则以下内容将使您获得具有TransactionId子节点的所有Confirmation元素,前提是这些元素使用默认命名空间(我使用xmlns="file:xyz"作为默认命名空间):

>>> tree.findall("//{{{0}}}Confirmation[{{{0}}}TransactionId]".format(tree.getroot().nsmap[None]))
[<Element {file:xyz}Confirmation at 0x7f534a85d1b8>, <Element {file:xyz}Confirmation at 0x7f534a85d128>]

当然有etree.ETXPath

>>> find=etree.ETXPath("//{{{0}}}Confirmation[starts-with({{{0}}}TransactionId, 'GTEREVIEW')]".format(tree.getroot().nsmap[None]))
>>> find(tree)
[<Element {file:xyz}Confirmation at 0x7f534a85d1b8>]

这允许您组合XPath和命名空间。

相关文章