Python LXML iterparse函数:在解析庞大的XML

时间:2017-08-21 05:33:03

标签: python xml parsing lxml

我在Python中使用LXML库解析大型XML(~500MB)。我已经使用BeautifulSoup和lxml-xml解析器来处理小文件。但是当我遇到巨大的XML时,它只读取整个文件一次,然后解析它是低效的。

我需要解析XML以获取叶子路径的根(最外面的标记除外) 例如

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE A>
<A>
    <B>
        <C>
            abc
        </C>
        <D>
            abd
        </D>
    </B>
</A>

上面的XML应该给出键和值作为输出(root到叶子路径)。

A.B.C = abc
 A.B.D = abd

这是我编写的用于解析它的代码:
(ignore1和ignore2是需要忽略的标记,tu.clean_text()是删除不必要字符的函数)

def fast_parser(filename, keys, values, ignore1, ignore2):
    context = etree.iterparse(filename, events=('start', 'end',))

    path = list()
    i = 0
    lastevent = ""
    for event, elem in context:
        i += 1
        tag = elem.tag if "}" not in elem.tag else elem.tag.split('}', 1)[1]

        if tag == ignore1 or tag == ignore2:
            pass
        elif event == "start":
            path.append(tag)
        elif event == "end":
            if lastevent == "start":
                keys.append(".".join(path))
                values.append(tu.clean_text(elem.text))

            # free memory
            elem.clear()
            while elem.getprevious() is not None:
                del elem.getparent()[0]
            if len(path) > 0:
                path.pop()
        lastevent = event

    del context
    return keys, values

我已经引用了以下文章来解析大文件ibm.com/developerworks/xml/library/x-hiperfparse/#listing4

这是top命令的屏幕截图。对于~500 MB的XML文件,内存使用量超过2 GB。我怀疑内存没有被释放。 enter image description here

我已经完成了几个StackOverflow问题。但它没有帮助。请指教。

1 个答案:

答案 0 :(得分:1)

我从https://stackoverflow.com/a/7171543/131187获取了代码,删除了评论和打印语句,并添加了一个合适的func来获取此代码。我不想猜测处理500 Mb文件需要多长时间!

即使在撰写func时,我也没有采用原创作品,采用了原作者对xpath表达式'ancestor-or-self :: *'的使用,来提供你想要的绝对路径。 / p>

但是,由于此代码更符合原始脚本,因此可能不会泄漏内存。

import lxml.etree as ET

input_xml = 'temp.xml'
for line in open(input_xml).readlines():
    print (line[:-1])

def mod_fast_iter(context, func, *args, **kwargs):
    for event, elem in context:
        func(elem, *args, **kwargs)
        elem.clear()
        for ancestor in elem.xpath('ancestor-or-self::*'):
            while ancestor.getprevious() is not None:
                del ancestor.getparent()[0]
    del context

def func(elem):
    content = '' if not elem.text else elem.text.strip()
    if content:
        ancestors = elem.xpath('ancestor-or-self::*')
        print ('%s=%s' % ('.'.join([_.tag for _ in ancestors]), content))

print ('\nResult:\n')
context = ET.iterparse(open(input_xml , 'rb'), events=('end', ))
mod_fast_iter(context, func)

输出:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE A>
<A>
    <B>
        <C>
            abc
        </C>
        <D>
            abd
        </D>
    </B>
</A

Result:

A.B.C=abc
A.B.D=abd