使用Python ElementTree内存不足
编辑:未来有朋友看到这个, 我用的解决方案是切换到cElementTree。它不仅占用更少的内存,而且速度明显更快。
这个方法可以处理大约600MB大小的文件,超过这个大小我就会内存不足(我有一台16GB的电脑)。我该如何分块读取文件,或者一次读取一定比例的xml,或者有没有更省内存的方法呢?
import csv
import xml.etree.ElementTree as ET
from lxml import etree
import time
import sys
def main(argv):
start_time = time.time()
#file_name = 'sample.xml'
file_name = argv
root = ET.ElementTree(file=file_name).getroot()
csv_file_name = '.'.join(file_name.split('.')[:-1]) + ".txt"
print '\n'
print 'Output file:'
print csv_file_name
with open(csv_file_name, 'w') as file_:
writer = csv.writer(file_, delimiter="\t")
header = [ <the names of the tags here> ]
writer.writerow(header)
tags = [
<bunch of xml tags here>
]
#write the values
# for index in range(8,1000):
for index in range(3,len(root)):
#print index
row=[]
for tagindex,val in enumerate(tags):
searchQuery = "tags"+tags[tagindex]
# print searchQuery
# print root[index]
# print root[index].find(searchQuery).text
if (root[index].find(searchQuery) is None) or (root[index].find(searchQuery).text == None):
row.extend([""])
#print tags[tagindex]+" blank"
else:
row.extend([root[index].find(searchQuery).text])
#print tags[tagindex]+" "+root[index].find(searchQuery).text
writer.writerow(row)
#for i,child in enumerate(root):
#print root[i]
print '\nNumber of elements is: %s' % len(root)
print '\nTotal run time: %s seconds' % (time.time() - start_time)
if __name__ == "__main__":
main(sys.argv[1])
3 个回答
1
使用ElementTree.iterparse来解析你的XML数据。可以查看文档获取帮助。
3
一些小提示:
- 使用
lxml
,它的性能非常好 - 使用
iterparse
,这样可以逐块处理你的文档
不过,iterparse
可能会让你感到意外,导致内存消耗过高。为了避免这个问题,你需要清除已经处理过的项目的引用,具体方法可以参考我最喜欢的一篇文章,关于如何有效使用 lxml
示例脚本 fastiterparse.py
使用优化过的 iterparse
安装 docopt
和 lxml
$ pip install lxml docopt
编写脚本:
"""For all elements with given tag prints value of selected attribute
Usage:
fastiterparse.py <xmlfile> <tag> <attname>
fastiterparse.py -h
"""
from lxml import etree
from functools import partial
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 printattname(elem, attname):
print elem.attrib[attname]
def main(fname, tag, attname):
fun = partial(printattname, attname=attname)
with open(fname) as f:
context = etree.iterparse(f, events=("end",), tag=tag)
fast_iter(context, fun)
if __name__ == "__main__":
from docopt import docopt
args = docopt(__doc__)
main(args["<xmlfile>"], args["<tag>"], args["<attname>"])
尝试调用它:
$ python fastiterparse.py
Usage:
fastiterparse.py <xmlfile> <tag> <attname>
fastiterparse.py -h
在你的文件上使用它:
$ python fastiterparse.py large.xml ElaboratedRecord id
rec26872
rec25887
rec26873
rec26874
总结(使用 fast_iter
方法)
主要的要点是 fast_iter
函数(或者至少要记得 clear
不再使用的元素,删除它们,最后删除 context
)
测量结果显示,在某些情况下,脚本运行的速度可能会比不使用 clear
和 del
时稍慢,但差别并不大。当内存成为限制时,优化版本会变得更快,如果内存用完了,选择就不多了。
3
使用cElementTree来代替ElementTree。
把你原来的导入语句换成:import xml.etree.cElementTree as ET