使用 Python 从大文件中解析 XML

Pep*_*zza 1 python xml beautifulsoup

我有一个 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)
Run Code Online (Sandbox Code Playgroud)

该文件不是复杂的 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>
Run Code Online (Sandbox Code Playgroud)

我正在使用的这个文件更有可能继续增长,因此最好按块读取它或不加载整个文件。我需要帮助解决这个问题。也许 BS4 以外的其他一些包更快,是否有其他包或避免将整个文件加载到内存中的方法?

Mar*_*ers 5

您想在这里切换到xml.etree.ElementTree()API;它有一个iterparse()迭代解析功能

for event, elem in iterparse(source):
    if elem.tag == "record":
        # do something with the <record> element

        elem.clear()  # clean up
Run Code Online (Sandbox Code Playgroud)

由于您已经在使用 BeautifulSoup XML 模式,因此您必须已经lxml安装。lxml实现相同的 API,但在 C 中。请参阅lxml iterparse()文档

请阅读为什么 lxml.etree.iterparse() 占用了我所有的内存?确保在使用时正确清除元素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()
Run Code Online (Sandbox Code Playgroud)