Python:如何处理大型 XML 文件,其中包含 1 个根目录中的大量子文件
Python: How to process large XML file with a lot of childs in 1 root
我有 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 逐个处理元素?
您可以使用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')
基本上有三种标准的解析方法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 解析器。
考虑 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']]
我有 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 逐个处理元素?
您可以使用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')
基本上有三种标准的解析方法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 解析器。
考虑 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']]