[英]using lxml and iterparse() to parse a big (+- 1Gb) XML file
我必須解析具有如下結構的 1Gb XML 文件,並提取標簽“作者”和“內容”中的文本:
<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。 請幫忙!
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for child in element:
print(child.tag, child.text)
element.clear()
最后清除將阻止您使用太多內存。
[更新:] 將“......之間的一切都作為一個字符串”我想你想要一個:
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()
對於未來的搜索者:這里的最佳答案建議在每次迭代時清除元素,但這仍然會給您留下不斷增加的空元素集,這些空元素將在內存中慢慢積累:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for child in element:
print(child.tag, child.text)
element.clear()
^ 這不是一個可擴展的解決方案,尤其是當您的源文件越來越大時。 更好的解決方案是獲取根元素,並在每次加載完整記錄時清除它。 這將保持內存使用相當穩定(我會說低於 20MB)。
這是一個不需要查找特定標簽的解決方案。 此函數將返回一個生成器,該生成器生成根節點(例如<Database>
)下的所有第一個子節點(例如<BlogPost>
元素)。 它通過記錄根節點之后第一個標簽的開始,然后等待相應的結束標簽,產生整個元素,然后清除根節點來實現這一點。
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()
對於這樣的事情,我更喜歡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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.