具有大文件的lxml:根据属性

时间:2019-10-28 00:30:37

标签: python xml backup lxml large-files

我要解决的高级问题是我有一个1.5 GB的SMS数据转储,并且试图过滤文件以仅保留与单个联系人之间的消息。

我正在Python中使用lxml解析文件,但请告诉我是否有更好的选择。

XML文件的结构如下:

SMSES (root node)
  'count': 'xxxx',
  (Children):
      MMS
          'address': 'xxxx',
          'foo':     'bar',
           ... : ...,
           (Children)
               'other fields': 'that _do not_ specify address',
      MMS
          'address': 'xxxx',
          'foo':     'bar',
           ... : ...,
           (Children)
               'other fields': 'that _do not_ specify address'

即,我想遍历根节点的子节点,对于“地址”不匹配特定值的每个MMS,都应删除该MMS及其所有后代(子节点倾向于保存图像等项。 )。

我尝试过的事情:

我发现了这样的问题/答案:how to remove an element in lxml

但是这些线程往往具有简单的示例,没有嵌套元素。

  • 我不清楚如何使用tree.xpath()查找不匹配与值匹配的元素
  • 我不清楚是否调用remove(item)是否删除了该项目的后代(在本例中是我想要的)。

我尝试了一种非常幼稚的方法,在该方法中,我获得了一个迭代器,然后遍历树,并随即删除了元素:

from lxml.etree import XMLParser, parse
p = XMLParser(huge_tree=True)
tree = parse('backup.xml', parser=p)

it = tree.iter()
item = next(it) # consume root node

for item in it:
    if item.attrib['address'] != '0000':
        item.getparent().remove(item)

此脚本的问题在于,迭代器执行DFS,而MMS元素的子元素具有地址字段。所以,我在寻找:

  • 完成任务的最有效+最简单的方法是什么?
  • 否则,如何强制tree.iter()仅在根的一级邻居上给我一个BFS迭代器?
  • remove(item)是否确实删除了所有后代,还是将子代附加到父代?

感谢您抽出宝贵的时间阅读。抱歉,如果这是一个幼稚的问题-解析XML文件并不是我的头等大事,而LXML文档对于我来说是一个新手,很难理解。

谢谢!

1 个答案:

答案 0 :(得分:0)

上周有一个新版本的Saxon / C具有Python语言绑定功能,并结合了XSLT 3.0流功能:这是一个非常新的软件,但您可以尝试一下(使用可从saxonica.com获得的Saxon-EE评估许可证。 )。样式表非常简单:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0">

<xsl:mode streamable="yes"/>

<xsl:template match="/">
<SMSES>
   <xsl:copy-of select="SMS[@address='specific value']"/>
</SMSES>
</xsl:template>

</xsl:transform>

不幸的是,您已经抽象了XML,所以我无法确定“ address”实际上是元素还是属性,并且在流式传输时有很大的不同。我在这里假设它是一个属性,但是如果您提供真实的XML示例,那么我可以帮助您产生一些真实的XSLT代码。

如果没有真正的必须从Python运行的约束,您也可以使用已建立的Saxon / Java产品直接从命令行运行它。但无论哪种方式,流媒体都需要Saxon企业版。