我是python新手。我正在尝试使用lxml在我的python模块中解析一个巨大的xml文件。尽管在每个循环结束时清除了元素,但我的内存仍然猛增并使应用程序崩溃。我确定我在这里缺少什么。请帮助我弄清楚那是什么。
以下是我正在使用的主要功能-
from lxml import etree
def parseXml(context,attribList):
for _, element in context:
fieldMap={}
rowList=[]
readAttribs(element,fieldMap,attribList)
readAllChildren(element,fieldMap,attribList)
for row in rowList:
yield row
element.clear()
def readAttribs(element,fieldMap,attribList):
for atrrib in attribList:
fieldMap[attrib]=element.get(attrib,'')
def readAllChildren(element,fieldMap,attribList,rowList):
for childElem in element:
readAttribs(childEleme,fieldMap,attribList)
if len(childElem) > 0:
readAllChildren(childElem,fieldMap,attribList)
rowlist.append(fieldMap.copy())
childElem.clear()
def main():
attribList=['name','age','id']
context=etree.iterparse(fullFilePath, events=("start",))
for row in parseXml(context,attribList)
print row
谢谢!!
示例xml和嵌套字典-
<root xmlns='NS'>
<Employee Name="Mr.ZZ" Age="30">
<Experience TotalYears="10" StartDate="2000-01-01" EndDate="2010-12-12">
<Employment id = "1" EndTime="ABC" StartDate="2000-01-01" EndDate="2002-12-12">
<Project Name="ABC_1" Team="4">
</Project>
</Employment>
<Employment id = "2" EndTime="XYZ" StartDate="2003-01-01" EndDate="2010-12-12">
<PromotionStatus>Manager</PromotionStatus>
<Project Name="XYZ_1" Team="7">
<Award>Star Team Member</Award>
</Project>
</Employment>
</Experience>
</Employee>
</root>
ELEMENT_NAME='element_name'
ELEMENTS='elements'
ATTRIBUTES='attributes'
TEXT='text'
xmlDef={ 'namespace' : 'NS',
'content' :
{ ELEMENT_NAME: 'Employee',
ELEMENTS: [{ELEMENT_NAME: 'Experience',
ELEMENTS: [{ELEMENT_NAME: 'Employment',
ELEMENTS: [{
ELEMENT_NAME: 'PromotionStatus',
ELEMENTS: [],
ATTRIBUTES:[],
TEXT:['PromotionStatus']
},
{
ELEMENT_NAME: 'Project',
ELEMENTS: [{
ELEMENT_NAME: 'Award',
ELEMENTS: {},
ATTRIBUTES:[],
TEXT:['Award']
}],
ATTRIBUTES:['Name','Team'],
TEXT:[]
}],
ATTRIBUTES: ['TotalYears','StartDate','EndDate'],
TEXT:[]
}],
ATTRIBUTES: ['TotalYears','StartDate','EndDate'],
TEXT:[]
}],
ATTRIBUTES: ['Name','Age'],
TEXT:[]
}
}
看来您遵循了一些很好的建议lxml
,尤其是etree.iterparse(..)
,但是我认为您的实现从错误的角度来解决问题。的想法iterparse(..)
是摆脱收集和存储数据,而是在读取标签时进行处理。您的readAllChildren(..)
功能是将所有内容保存到中rowList
,该内容不断增长以覆盖整个文档树。我做了一些更改以显示正在发生的事情:
from lxml import etree
def parseXml(context,attribList):
for event, element in context:
print "%s element %s:" % (event, element)
fieldMap = {}
rowList = []
readAttribs(element, fieldMap, attribList)
readAllChildren(element, fieldMap, attribList, rowList)
for row in rowList:
yield row
element.clear()
def readAttribs(element, fieldMap, attribList):
for attrib in attribList:
fieldMap[attrib] = element.get(attrib,'')
print "fieldMap:", fieldMap
def readAllChildren(element, fieldMap, attribList, rowList):
for childElem in element:
print "Found child:", childElem
readAttribs(childElem, fieldMap, attribList)
if len(childElem) > 0:
readAllChildren(childElem, fieldMap, attribList, rowList)
rowList.append(fieldMap.copy())
print "len(rowList) =", len(rowList)
childElem.clear()
def process_xml_original(xml_file):
attribList=['name','age','id']
context=etree.iterparse(xml_file, events=("start",))
for row in parseXml(context,attribList):
print "Row:", row
使用一些伪数据运行:
>>> from cStringIO import StringIO
>>> test_xml = """\
... <family>
... <person name="somebody" id="5" />
... <person age="45" />
... <person name="Grandma" age="62">
... <child age="35" id="10" name="Mom">
... <grandchild age="7 and 3/4" />
... <grandchild id="12345" />
... </child>
... </person>
... <something-completely-different />
... </family>
... """
>>> process_xml_original(StringIO(test_xml))
start element: <Element family at 0x105ca58>
fieldMap: {'age': '', 'name': '', 'id': ''}
Found child: <Element person at 0x105ca80>
fieldMap: {'age': '', 'name': 'somebody', 'id': '5'}
len(rowList) = 1
Found child: <Element person at 0x105c468>
fieldMap: {'age': '45', 'name': '', 'id': ''}
len(rowList) = 2
Found child: <Element person at 0x105c7b0>
fieldMap: {'age': '62', 'name': 'Grandma', 'id': ''}
Found child: <Element child at 0x106e468>
fieldMap: {'age': '35', 'name': 'Mom', 'id': '10'}
Found child: <Element grandchild at 0x106e148>
fieldMap: {'age': '7 and 3/4', 'name': '', 'id': ''}
len(rowList) = 3
Found child: <Element grandchild at 0x106e490>
fieldMap: {'age': '', 'name': '', 'id': '12345'}
len(rowList) = 4
len(rowList) = 5
len(rowList) = 6
Found child: <Element something-completely-different at 0x106e4b8>
fieldMap: {'age': '', 'name': '', 'id': ''}
len(rowList) = 7
Row: {'age': '', 'name': 'somebody', 'id': '5'}
Row: {'age': '45', 'name': '', 'id': ''}
Row: {'age': '7 and 3/4', 'name': '', 'id': ''}
Row: {'age': '', 'name': '', 'id': '12345'}
Row: {'age': '', 'name': '', 'id': '12345'}
Row: {'age': '', 'name': '', 'id': '12345'}
Row: {'age': '', 'name': '', 'id': ''}
start element: <Element person at 0x105ca80>
fieldMap: {'age': '', 'name': '', 'id': ''}
start element: <Element person at 0x105c468>
fieldMap: {'age': '', 'name': '', 'id': ''}
start element: <Element person at 0x105c7b0>
fieldMap: {'age': '', 'name': '', 'id': ''}
start element: <Element child at 0x106e468>
fieldMap: {'age': '', 'name': '', 'id': ''}
start element: <Element grandchild at 0x106e148>
fieldMap: {'age': '', 'name': '', 'id': ''}
start element: <Element grandchild at 0x106e490>
fieldMap: {'age': '', 'name': '', 'id': ''}
start element: <Element something-completely-different at 0x106e4b8>
fieldMap: {'age': '', 'name': '', 'id': ''}
读取起来有些困难,但是您可以看到它是在第一遍中从根标签开始向下爬整棵树,rowList
为整个文档中的每个元素建立起来的。您还会注意到它甚至没有停在那儿,因为element.clear()
调用是
在中 的yield
语句 之后进行 的parseXml(..)
,直到第二次迭代(即树中的下一个元素)才会执行。
一个简单的解决方法是让它iterparse(..)
完成工作:迭代解析!以下内容将提取相同的信息并对其进行增量处理:
def do_something_with_data(data):
"""This just prints it out. Yours will probably be more interesting."""
print "Got data: ", data
def process_xml_iterative(xml_file):
# by using the default 'end' event, you start at the _bottom_ of the tree
ATTRS = ('name', 'age', 'id')
for event, element in etree.iterparse(xml_file):
print "%s element: %s" % (event, element)
data = {}
for attr in ATTRS:
data[attr] = element.get(attr, u"")
do_something_with_data(data)
element.clear()
del element # for extra insurance
在相同的伪XML上运行:
>>> print test_xml
<family>
<person name="somebody" id="5" />
<person age="45" />
<person name="Grandma" age="62">
<child age="35" id="10" name="Mom">
<grandchild age="7 and 3/4" />
<grandchild id="12345" />
</child>
</person>
<something-completely-different />
</family>
>>> process_xml_iterative(StringIO(test_xml))
end element: <Element person at 0x105cc10>
Got data: {'age': u'', 'name': 'somebody', 'id': '5'}
end element: <Element person at 0x106e468>
Got data: {'age': '45', 'name': u'', 'id': u''}
end element: <Element grandchild at 0x106e148>
Got data: {'age': '7 and 3/4', 'name': u'', 'id': u''}
end element: <Element grandchild at 0x106e490>
Got data: {'age': u'', 'name': u'', 'id': '12345'}
end element: <Element child at 0x106e508>
Got data: {'age': '35', 'name': 'Mom', 'id': '10'}
end element: <Element person at 0x106e530>
Got data: {'age': '62', 'name': 'Grandma', 'id': u''}
end element: <Element something-completely-different at 0x106e558>
Got data: {'age': u'', 'name': u'', 'id': u''}
end element: <Element family at 0x105c6e8>
Got data: {'age': u'', 'name': u'', 'id': u''}
这将大大提高脚本的速度和内存性能。另外,通过钩住'end'
事件,您可以随时清除和删除元素,而不必等到所有子级都已处理完毕。
根据您的数据集,最好只处理某些类型的元素。根元素之一可能不是很有意义,其他嵌套元素也可能用填充很多数据{'age': u'', 'id': u'', 'name': u''}
。
顺便说一句,当我阅读“
XML”和“低内存”时,我的想法总是直接跳到SAX上,这是您可以解决此问题的另一种方法。使用内置xml.sax
模块:
import xml.sax
class AttributeGrabber(xml.sax.handler.ContentHandler):
"""SAX Handler which will store selected attribute values."""
def __init__(self, target_attrs=()):
self.target_attrs = target_attrs
def startElement(self, name, attrs):
print "Found element: ", name
data = {}
for target_attr in self.target_attrs:
data[target_attr] = attrs.get(target_attr, u"")
# (no xml trees or elements created at all)
do_something_with_data(data)
def process_xml_sax(xml_file):
grabber = AttributeGrabber(target_attrs=('name', 'age', 'id'))
xml.sax.parse(xml_file, grabber)
您必须根据哪种情况最适合您来评估这两个选项(如果您经常这样做,则可能要运行几个基准测试)。
确保跟进事情的进展!
实施上述任何一种解决方案都可能需要对代码的整体结构进行一些更改,但是您所拥有的一切仍然应该可行。例如,批量处理“行”,您可能需要:
def process_xml_batch(xml_file, batch_size=10):
ATTRS = ('name', 'age', 'id')
batch = []
for event, element in etree.iterparse(xml_file):
data = {}
for attr in ATTRS:
data[attr] = element.get(attr, u"")
batch.append(data)
element.clear()
del element
if len(batch) == batch_size:
do_something_with_batch(batch)
# Or, if you want this to be a genrator:
# yield batch
batch = []
if batch:
# there are leftover items
do_something_with_batch(batch) # Or, yield batch
为了在巨大的xml文件中执行XPATH查询,我阅读了许多喜欢VTD-xml的文章,因此我复制了这些文章中的代码: 但当我运行它时没有结果,所以这意味着XML文件没有映射到内存中。。。我的问题是如何在VTD-xml中强制映射xml文件?
问题内容: 我正在尝试将DMOZ内容/结构XML文件解析为MySQL,但是所有现有的脚本都已经很老了并且不能很好地工作。如何在PHP中打开大型(+ 1GB)XML文件进行解析? 问题答案: 只有两个php API真正适合处理大文件。第一个是旧的expatapi,第二个是较新的XMLreader函数。这些api读取连续流,而不是将整个树加载到内存中(这是simplexml和DOM所做的)。 例如,您
问题内容: 我的XML解析功能的简化版本在这里: 这会导致Python的内存不足,这没有任何意义。我真正存储的唯一内容是计数,一个整数。 看到内存和CPU使用率突然下降了吗?那是Python的惊人崩溃。至少它给了我一个(取决于我在循环中所做的事情,它给了我更多的随机错误,如)和堆栈跟踪而不是段错误。但是为什么会崩溃? 问题答案: 该文档确实告诉您“将XML节逐步地解析 到元素树中 (我的重点)”,
问题内容: 最近,我对算法产生了兴趣,并通过编写一个简单的实现,然后以各种方式对其进行了优化来开始探索它们。 我已经熟悉了用于分析运行时的标准Python模块(对于大多数事情,我发现IPython中的timeit magic函数就足够了),但是我也对内存使用感兴趣,因此我也可以探索这些折衷方案(例如,缓存先前计算的值与根据需要重新计算它们的表的成本)。是否有一个模块可以为我配置给定功能的内存使用情
本文向大家介绍Python中的html5lib和lxml解析器,包括了Python中的html5lib和lxml解析器的使用技巧和注意事项,需要的朋友参考一下 html5lib是用于解析HTML的纯Python库。它被设计为符合WHATWG HTML规范,所有主要的Web浏览器都实现了该规范。它可以解析HTML文档的几乎所有元素,将其分解为不同的标记和片段,可以针对各种用例将其过滤掉。它以与主要浏
我正在尝试获取XML文件并将其解析到数据库中。XML是用GZIP压缩的。GZIP文件是~8MB。当我在本地运行代码时,pythonw上的内存将被占用。exe的版本升级到整个系统(Windows 7)停止响应的级别,当我在线运行它时,它超过了Google App Engine上的内存限制。不确定文件是否太大或我是否做错了什么。任何帮助都将不胜感激! 使现代化 所以我试着按照BasicWolf的建议切
问题内容: 我想使用python解析一个简单的小型XML文件,但是在pyXML上的工作似乎已经停止了。如果可能的话,我想使用python 2.6。谁能推荐可以与2.6兼容的XML解析器? 谢谢 问题答案: 如果它既小又简单,则只需使用标准库即可: 这将返回实现标准文档对象模型API的DOM树 如果以后需要执行诸如模式验证或XPath查询之类的复杂操作,那么我建议使用第三方lxml模块,它是流行的l
我正在尝试用jackson SAX解析器实现JSON数组迭代器(请不要问为什么)。我的应用程序应该处理巨大的文件(高达5 MiB),这是一个问题。 这就是我初始化JsonParser和调用迭代器创建的方式。我创建了InputStream,使用放在\raw文件夹中的JSON初始化。 这是我的迭代器类。 它似乎运转良好...哦,等等。 我在某个巨大的文件中有3个部分(命名数组)。它首先成功解析(一个很