我必须使用如下结构解析1Gb XML文件,并在“Author”和“Content”标签中提取文本:
<Database>
<BlogPost>
<Date>MM/DD/YY</Date>
<Author>Last Name, Name</Author>
<Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
</BlogPost>
<BlogPost>
<Date>MM/DD/YY</Date>
<Author>Last Name, Name</Author>
<Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
</BlogPost>
[...]
<BlogPost>
<Date>MM/DD/YY</Date>
<Author>Last Name, Name</Author>
<Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
</BlogPost>
</Database>
到目前为止,我已经尝试了两件事:i)读取整个文件并使用.find(xmltag)和ii)使用lxml和iterparse()解析xml文件。 第一个选择我已经让它工作了,但它很慢。第二种选择我没有设法让它开始。
这是我所拥有的一部分:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
if element.tag == "BlogPost":
print element.text
else:
print 'Finished'
结果只是空格,没有文字。
我一定是做错了什么,但我无法理解。另外,如果它不够明显,我对python很新,这是我第一次使用lxml。请帮助!
答案 0 :(得分:24)
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for child in element:
print child.tag, child.text
element.clear()
最后的清除会阻止你使用太多内存。
[update:]获取“作为字符串之间的所有内容”我猜您想要其中一个:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
print etree.tostring(element)
element.clear()
或
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
print ''.join([etree.tostring(child) for child in element])
element.clear()
或者甚至是:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
print ''.join([child.text for child in element])
element.clear()
答案 1 :(得分:13)
对于未来的搜索者:这里的最佳答案建议在每次迭代时清除元素,但这仍然会留下一组不断增加的空元素,这些元素将在内存中慢慢积累:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for child in element:
print child.tag, child.text
element.clear()
^这不是一个可扩展的解决方案,尤其是当您的源文件变得越来越大时。更好的解决方案是获取 root 元素,并在每次加载完整记录时清除 。这将使内存使用保持相当稳定(我会说不到20MB)。
这是一个不需要查找特定标签的解决方案。此函数将返回一个生成器,该生成器在根节点下生成所有第一个子节点(例如<BlogPost>
元素)(例如<Database>
)。它通过在根节点之后记录第一个标记的开头,然后等待相应的结束标记,产生整个元素,然后清除根节点来完成此操作。
from lxml import etree
xmlfile = '/path/to/xml/file.xml'
def iterate_xml(xmlfile):
doc = etree.iterparse(xmlfile, events=('start', 'end'))
_, root = next(doc)
start_tag = None
for event, element in doc:
if event == 'start' and start_tag is None:
start_tag = element.tag
if event == 'end' and element.tag == start_tag:
yield element
start_tag = None
root.clear()
答案 2 :(得分:5)
我更喜欢XPath这样的事情:
In [1]: from lxml.etree import parse
In [2]: tree = parse('/tmp/database.xml')
In [3]: for post in tree.xpath('/Database/BlogPost'):
...: print 'Author:', post.xpath('Author')[0].text
...: print 'Content:', post.xpath('Content')[0].text
...:
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
但是,我不确定在处理大文件方面是否有所不同。关于这一点的评论将不胜感激。
按照自己的方式行事,
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for info in element.iter():
if info.tag in ('Author', 'Content'):
print info.tag, ':', info.text