用于解析小型、刚性结构 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:基准测试结果
- 解决方案 1:Python-循环树遍历(如在 OP 中)- 77.9 毫秒
- 解决方案 2:iterparse - 11.4 毫秒
- 解决方案 3:xml 解析和 xslt(无 csv 解析)- 11.9 毫秒
- 解决方案 4:cython 在 C 中使用 libxml2 链接 SAX 解析器(在 python 中读取文件)- 2.33ms
用于 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 </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> </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
循环处理。
根据哪个元素结束,正在设置变量 field1
和 field2
。这里的隐含假设是 <field1>
和 <field2>
只出现在 <element>
内部并且没有进一步嵌套。
如果这些东西是有保证的,那么当我们遇到结束时</element>
,这两个变量就包含了预期的字符串。如果这些事情不一定是真的,你需要在迭代过程中保持某种状态。
在 Python 中,这应该是尽可能快的,因为它完全依赖于在事件发生时解析事件,并且根本不使用任何 XPath。
我有一个 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:基准测试结果
- 解决方案 1:Python-循环树遍历(如在 OP 中)- 77.9 毫秒
- 解决方案 2:iterparse - 11.4 毫秒
- 解决方案 3:xml 解析和 xslt(无 csv 解析)- 11.9 毫秒
- 解决方案 4:cython 在 C 中使用 libxml2 链接 SAX 解析器(在 python 中读取文件)- 2.33ms
用于 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 </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> </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
循环处理。
根据哪个元素结束,正在设置变量 field1
和 field2
。这里的隐含假设是 <field1>
和 <field2>
只出现在 <element>
内部并且没有进一步嵌套。
如果这些东西是有保证的,那么当我们遇到结束时</element>
,这两个变量就包含了预期的字符串。如果这些事情不一定是真的,你需要在迭代过程中保持某种状态。
在 Python 中,这应该是尽可能快的,因为它完全依赖于在事件发生时解析事件,并且根本不使用任何 XPath。