Python:如何使用1根中的许多子进程处理大型XML文件

时间:2017-09-04 15:18:53

标签: python xml lxml

我有一个XML文件,其数据结构如

<report>
  <table>
    <detail name="John" surname="Smith">
    <detail name="Michael" surname="Smith">
    <detail name="Nick" surname="Smith">
    ... {a lot of <detail> elements}
  </table>
</report>

我需要检查属性为'name'=='surname'的元素。

XML文件大于1 GB,我在尝试 etree.parse(file)时出错。

如何使用Python和LXML逐个处理元素?

3 个答案:

答案 0 :(得分:2)

考虑iterparse,它允许您在构建树时处理元素。下面检查 name 属性是否等同于 surname 属性。使用if块进一步处理,例如有条件地将值附加到列表中:

import xml.etree.ElementTree as et

data = []
path = "/path/to/source.xml"

# get an iterable
context = et.iterparse(path, events=("start", "end"))

# turn it into an iterator
context = iter(context)

# get the root element
ev, root = next(context)

for ev, el in context:
    if ev == 'start' and el.tag == 'detail':
        print(el.attrib['name'] == el.attrib['surname'])
        data.append([el.attrib['name'], el.attrib['surname']])
        root.clear()

print(data)
# False
# False
# False

# [['John', 'Smith'], ['Michael', 'Smith'], ['Nick', 'Smith']]

答案 1 :(得分:1)

解析XML基本上有三种标准方法:

  • 构建内存中的Document Object Model(DOM) - 将整个文档加载到内存中,可以随意沿着树行走
  • 编写推送SAX解析器 - 文档处理成为一系列事件(开始标记,文本,结束标记,注释,处理指令等),您可以订阅其中的几个。您注册回调并运行解析。读取文档直到结束,但解析器不构建整个文档的内部表示。
  • 编写一个提取StAX解析器 - 解析器流式传输不同的事件,您按顺序处理所有事件,但可以随时停止(对于在文档开头解析XML元数据并停止处理很有用)

lxml是对libxml C库的绑定,它是DOM的实现,iterparse方法似乎是StAX方法的实现。 SAX解析器内置于python本身:https://docs.python.org/3.6/library/xml.sax.html

对于您的情况,标准方法是使用SAX解析器。

答案 2 :(得分:0)

可以使用iterparse方法,该方法用于处理大型xml文件。但是,您的文件具有特别简单的结构。使用iterparse会不必要地复杂化。

我将在一个脚本中提供两个答案。我通过展示如何使用lxml解析xml中的行来直接回答你的问题,并且我使用正则表达式提供了我认为可能更好的答案。

代码读取xml中的每一行,并忽略那些不以'try ... except开头的行。当脚本找到这样一行时,它会从lxml将其传递给etree进行解析,然后显示该行的属性。之后,它使用正则表达式来解析相同的属性并显示它们。

我强烈怀疑正则表达式会更快。

>>> from lxml import etree
>>> report = '''\
... <report>
...     <table>
...         <detail name="John" surname="Smith">
...         <detail name="Michael" surname="Smith">
...         <detail name="Nick" surname="Smith">
...     </table>
... </report>'''
>>> import re
>>> re.search(r'name="([^"]*)"\s+surname="([^"]*)', line).groups()
('John', 'Smith')
>>> for line in report.split('\n'):
...     if line.strip().startswith('<detail'):
...         tree = etree.fromstring(line.replace('>', '/>'))
...         tree.attrib['name'], tree.attrib['surname']
...         re.search(r'name="([^"]*)"\s+surname="([^"]*)', line).groups()
...         
('John', 'Smith')
('John', 'Smith')
('Michael', 'Smith')
('Michael', 'Smith')
('Nick', 'Smith')
('Nick', 'Smith')