lxml iterparse 清理后仍占用内存
我正在尝试解析XML文件。第一次使用iterparse的时候一切正常,但第二次开始占用内存。如果我去掉第一次的iterparse,结果也没有变化。XML文件是有效的。
def clear_element(e):
e.clear()
while e.getprevious() is not None:
del e.getparent()[0]
def import_xml(request):
f = 'file.xml'
offers = etree.iterparse(f, events=('end',), tag='offer')
for event, offer in offers:
# processing
# works correctly
clear_element(offer)
categories = etree.iterparse(f, events=('end',), tag='category')
for event, category in categories:
# using memory
clear_element(category)
XML内容:
<shop>
<categories>
<category>name</category>
<category>name</category>
<category>name</category>
~ 1000 categories
</categories>
<offers>
<offer>
<inner_tag>data</inner_tag>
<inner_tag>data</inner_tag>
</offer>
<offer>
<inner_tag>data</inner_tag>
<inner_tag>data</inner_tag>
</offer>
~ 450000 offers
</offers>
</shop>
2 个回答
0
我之前也在和 iterparse
斗争了一段时间,现在终于觉得自己知道怎么正确使用它了,所以我来分享一下我的经验:
确保使用
cElementTree
的实现版本。在处理过程中,记得清理掉那些你不需要的元素。如果你的 XML 结构很复杂,层次很深,这一点尤其重要。
假设你的 XML
里有这样的额外节点:
<offers>
<offer>
<inner_tag>data</inner_tag>
<i2>
<i3>1000 characters of something</i3>
</i2>
<inner_tag>data</inner_tag>
</offer>
</offers>
那么你的代码应该像这样:
def import_xml(request):
f = 'file.xml'
elements = etree.iterparse(f, events=('end',))
for event, element in elements:
if element.tag == 'offer':
# handle offer ...
elif element.tag == 'category':
# handle category ...
elif element.tag != 'i2':
continue
element.clear()
这样一来,你就可以在处理 <offers>
中的其他元素时,完全忽略掉 <i2>
节点及其内容。
不过我发现 element.getparent().remove(element)
在我的代码中不管用(会报 AttributeError)。
3
你在解析文件的时候做了两次。第一次你保留了所有的 category
标签,丢掉了 offer
标签,这样处理1000个 category
标签其实占用的内存不多。
但是第二次你只丢掉了 category
标签,却保留了所有的450000个 offer
标签,这就是为什么构建树的时候会需要很多内存。
在这种情况下,最好不要在 iterparse
中使用 tag
参数,而是直接检查标签名,同时丢掉所有不需要的标签:
def import_xml(request):
f = 'file.xml'
elements = etree.iterparse(f, events=('end',))
for event, element in elements:
if element.tag == 'offer':
# handle offer ...
elif element.tag == 'category':
# handle category ...
else:
continue
element.clear()
element.getparent().remove(element)
注意:仅仅调用 element.clear()
而不把它从父元素中删除,仍然会让清空的元素留在内存中,作为构建树的一部分。可能其实不需要使用 clear
...