适用于80 + GB XML的Python sax到lxml
您将如何使用sax读取XML文件并将其转换为lxml etree.iterparse元素?
为了提供问题的概述,我使用lxml构建了XML提取工具,用于XML提要,其大小范围为25-500MB,需要每两天进行一次提取,但是需要执行一次一次的文件大小为60-100GB。
我选择使用lxml的依据是,详细说明了一个节点的大小不得超过4 -8 GB,我认为这将允许该节点被读入内存并在完成后清除。
如果下面的代码概述
elements = etree.iterparse(
self._source, events = ('end',)
)
for event, element in elements:
finished = True
if element.tag == 'Artist-Types':
self.artist_types(element)
def artist_types(self, element):
"""
Imports artist types
:param list element: etree.Element
:returns boolean:
"""
self._log.info("Importing Artist types")
count = 0
for child in element:
failed = False
fields = self._getElementFields(child, (
('id', 'Id'),
('type_code', 'Type-Code'),
('created_date', 'Created-Date')
))
if self._type is IMPORT_INC and has_artist_type(fields['id']):
if update_artist_type(fields['id'], fields['type_code']):
count = count + 1
else:
failed = True
else:
if create_artist_type(fields['type_code'],
fields['created_date'], fields['id']):
count = count + 1
else:
failed = True
if failed:
self._log.error("Failed to import artist type %s %s" %
(fields['id'], fields['type_code'])
)
self._log.info("Imported %d Artist Types Records" % count)
self._artist_type_count = count
self._cleanup(element)
del element
让我知道是否可以添加任何类型的说明。
-
iterparse
是一个迭代解析器。它会发出Element
对象和事件,并在Element
解析时逐步构建整个树,因此最终它将在内存中存储整个树。但是,有一个有限的内存行为很容易:在解析它们时删除不再需要的元素。
典型的“巨型xml”工作负载是单个根元素,带有大量代表记录的子元素。我认为这是您正在使用的XML结构吗?
通常,足以
clear()
清空您正在处理的元素。您的内存使用量会增加一点,但不是很多。如果您的文件很大,那么即使是空Element
对象也将消耗过多的空间,在这种情况下,您还必须删除以前看到的Element
对象。请注意,您不能安全地删除当前元素。该lxml.etree.iterparse
文档描述了这种技术。在这种情况下,您将在每次
</record>
找到a时处理一条记录,然后删除所有先前的记录元素。以下是使用无限长的XML文档的示例。它将在解析时打印该进程的内存使用情况。请注意,内存使用情况稳定并且不会继续增长。
from lxml import etree import resource class InfiniteXML (object): def __init__(self): self._root = True def read(self, len=None): if self._root: self._root=False return "<?xml version='1.0' encoding='US-ASCII'?><records>\n" else: return """<record>\n\t<ancestor attribute="value">text value</ancestor>\n</record>\n""" def parse(fp): context = etree.iterparse(fp, events=('end',)) for action, elem in context: if elem.tag=='record': # processing goes here pass #memory usage print resource.getrusage(resource.RUSAGE_SELF).ru_maxrss # cleanup # first empty children from current element # This is not absolutely necessary if you are also deleting siblings, # but it will allow you to free memory earlier. elem.clear() # second, delete previous siblings (records) while elem.getprevious() is not None: del elem.getparent()[0] # make sure you have no references to Element objects outside the loop parse(InfiniteXML())