lxml的iterparse尝试将整个文件加载到内存中
我正在尝试解析一个非常大的XML文件,所以我决定使用lxml.iterparse这个方法,具体可以参考这里。
我的代码大概是这样的:
import sys
from lxml import etree
def fast_iter(context, func):
for event, elem in context:
func(elem)
elem.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
del context
def launchArticleProcessing(elem):
print elem
context = etree.iterparse(sys.argv[1], events=('end',), tag='text')
fast_iter(context, launchArticleProcessing)
我这样调用它:python lxmlwtf.py "/path/to/my/file.xml"
但是内存一下子就被占满了(直到我不得不终止这个进程,因为这个文件根本装不下),而且什么都没有打印出来。我到底漏掉了什么呢?
3 个回答
0
根据我的经验,定期调用垃圾回收器可以帮助很多。
你可以试试下面这样的代码:
import sys
from lxml import etree
def fast_iter(context, func):
for i, (event, elem) in enumerate(context):
# Garbage collect after every 100 elements
if i % 100 == 0:
gc.collect()
func(elem)
elem.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
del context
def launchArticleProcessing(elem):
print elem
context = etree.iterparse(sys.argv[1], events=('end',), tag='text')
fast_iter(context, launchArticleProcessing)
2
我在这里回答过一个非常相似的问题:lxml和fast_iter占用所有内存。主要原因是因为 lxml.etree
会把所有没有被明确捕获的元素都保存在内存中。所以你需要手动清理这些内容。
我做的事情是没有过滤你想要的标签的事件:
context = etree.iterparse(open(filename,'r'),events=('end',))
然后手动解析并清理其他内容:
for (event,elem) in progress.bar(context):
if elem.tag == 'text':
# do things here
# every element gets cleared here
elem.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
del context
1
我之前说错了,正如我在评论中解释的那样。lxml会把文件加载到内存中,直到找到一个和给定标签对应的项目。
如果找不到这个标签(比如因为lxml在标签前加了命名空间),它就会一直把文件加载到内存中,这就是问题所在。
所以解决办法就是提供一个正确的标签!我通过在文件的一部分上使用普通解析器找到了正确的标签。