我有一个50MB的xml文件,我需要从中读取一些数据。我的方法是使用Beautifulsoup 4,因为我已经使用该包一段时间了。这段代码显示了我一直在做的事情:
from bs4 import Beautifulsoup
# since the file is big, this line takes minutes to execute
soup = Beautifulsoup(open('myfile.xml'), 'xml')
items = soup.find_all('item')
for item in items:
name = item['name']
status = item.find('status').text
description = item.find('desc').text
refs = item.findAll('ref')
data = []
for ref in refs:
if 'url' in ref.attrs:
data.append('%s:%s' % (ref['source'], ref['url']))
else:
data.append('%s:%s' % (ref['source'], ref.text))
do_something(data)
文件并不复杂xml,我只需要读取每个<item>
条目上的每个数据:
<item type="CVE" name="some-name" seq="1999-0003">
<status>Entry</status>
<desc>A description goes here.</desc>
<refs>
<ref source="NAI">NAI-29</ref>
<ref source="CERT">CA-98.11.tooltalk</ref>
<ref source="SGI" url="example.com">Some data</ref>
<ref source="XF">aix-ttdbserver</ref>
<ref source="XF">tooltalk</ref>
</refs>
</item>
我正在使用的这个文件更有可能继续增长,所以用块读取它或者不加载整个文件会很棒。我需要帮助解决这个问题。也许除了BS4之外的其他一些软件包速度更快,是否还有一些其他软件包或方法可以避免将整个文件加载到内存中?
答案 0 :(得分:3)
您想在此处切换到xml.etree.ElementTree()
API;它有一个iterparse()
iterative parsing function:
for event, elem in iterparse(source):
if elem.tag == "record":
# do something with the <record> element
elem.clear() # clean up
由于您已经在使用BeautifulSoup XML模式,因此您必须已安装lxml
。 lxml
实现相同的API,但在C中。请参阅lxml
iterparse()
documentation。
请务必阅读Why is lxml.etree.iterparse() eating up all my memory?以确保在使用lxml
时正确清除元素。
默认情况下只发出end
个事件;整个标记已被解析,包括子节点。您可以对<item>
元素使用此功能:
for event, elem in iterparse(source):
if elem.tag == "item":
status = elem.find('status').text
desc = elem.find('desc').text
refs = {r.get('source'): r.text for r in elem.findall('./refs/ref')}
elem.clear()
答案 1 :(得分:0)
请详细了解lxml,它是类似XML的数据查询的瑞士刀。它可以使用多个引擎,包括BeautifoulSoup,您可以使用XPath查询数据,并在XML文件中执行许多高级任务。
这是一种从documentation
解析大文件的方法with open('xmlfile.xml') as f:
for event, element in etree.iterparse(f, events=("start", "end")):
print("%5s, %4s, %s" % (event, element.tag, element.text))
尽管文档显示了许多与解析器交互以仅生成子树的方法。