适用于80 + GB XML的Python sax到lxml

发布于 2021-01-29 18:15:55

您将如何使用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

让我知道是否可以添加任何类型的说明。

关注者
0
被浏览
51
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    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())
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看