用于解析小型、刚性结构 XML 文件的最快 lxml 接口

Fastest lxml interface for parsing small, rigidly structured XML files

我有一个 xml 文档,我希望解析它的结构如下:

<base>
  <intermediate>
    <element>
      <field1>some_text</field2>
      <field2>more_text</field2>
    </element>
    # <element> repeated about 2000 times
  </intermediate>
</base>

我的第一个方法是使用 lxml 的 xslt 接口将其转换为 CSV,然后将此 csv 读入 python 列表。

在表现不尽如人意后,我想试试下面的:

for intermediate in root.xpath('./intermediate'):
    for element in index.xpath('./element[field2/text()]'):
        field1 = element.xpath('field1/text()')[0]
        field2 = element.xpath('field2/text()')[0]

事实证明这要慢得多。 - 这不足为奇。

不过,我是否以最佳方式使用 lxml?它的功能如此丰富,以至于我无法确定我没有为给定的问题选择一个糟糕的界面。


编辑 1:基准测试结果

用于 xslt 的代码:

xml = lxml.etree.parse('my_xml_file.xml')
xsl = lxml.etree.parse("my_xsl_file.xsl")
transformer = lxml.etree.XSLT(xsl)    
result = transformer(xml)
csv_data = str(result)

使用的 XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="UTF-8"/>

    <xsl:template match="/base">
        <xsl:text>field1|field2&#10;</xsl:text>
        <xsl:apply-templates select='intermediate/element[field2/text()]'/>
    </xsl:template>

    <xsl:template match="element">
        <xsl:value-of select="field1"/><xsl:text>|</xsl:text>
        <xsl:value-of select="field2"/><xsl:text>&#10;</xsl:text>
    </xsl:template>

</xsl:stylesheet>

你可以试试 iterparse:

from lxml import etree as ET

def parse_xml(file_name, tag_name):
    for event, element in ET.iterparse(file_name, tag = tag_name):
        if (element.xpath('field2/text()')):
            yield (element.find('field1').text, element.find('field2').text)

result = [tuple for tuple in parse_xml('input-file.xml', 'element')]

print(result)

以下迭代解析 XML 文件:

import lxml.etree as ET

with open("input.xml", "rb") as f:
    context = ET.iterparse(f)
    for action, elem in context:
        if elem.tag == "field1":
            field1 = elem.text

        if elem.tag == "field2":
            field2 = elem.text

        if elem.tag == "element":
            print(field1, field2)
            field1 = None
            field2 = None

这里 lxml 以基于事件的方式工作。每次遇到结束元素 (</xyz>) 时,都会生成一个 end 事件并由 for 循环处理。

根据哪个元素结束,正在设置变量 field1field2。这里的隐含假设是 <field1><field2> 只出现在 <element> 内部并且没有进一步嵌套。

如果这些东西是有保证的,那么当我们遇到结束时</element>,这两个变量就包含了预期的字符串。如果这些事情不一定是真的,你需要在迭代过程中保持某种状态。

在 Python 中,这应该是尽可能快的,因为它完全依赖于在事件发生时解析事件,并且根本不使用任何 XPath。