python:是否有一个作为生成器实现的XML解析器?

jld*_*ont 8 python xml parsing generator

我想"动态"解析一个大的XML文件.我想使用python生成器来执行此操作.我尝试过"xml.etree.cElementTree"的"iterparse"(这真的很不错),但仍然不是生成器.

其他建议?

u0b*_*6ae 15

xml.etree.cElementTree接近正确使用的发电机; 默认情况下,您会在"结束"事件后收到每个元素,此时您可以处理它.如果在处理后不需要元素,则应该对元素使用element.clear(); 从而节省了内存.


这是一个完整的例子,我的意思是,解析Rhythmbox(音乐播放器)库.我使用(c)ElementTree的iterparse,并且对于每个处理过的元素,我调用element.clear(),以便节省大量内存.(顺便说一句,下面的代码是一个继任者一些萨克斯代码做同样的事情,在cElementTree的解决方案是自1浮雕)的代码简洁,并表示我需要什么,仅此而已2)这是3倍的速度,3)它使用更少的内存.)

import os
import xml.etree.cElementTree as ElementTree
NEEDED_KEYS= set(("title", "artist", "album", "track-number", "location", ))

def _lookup_string(string, strmap):
    """Look up @string in the string map,
    and return the copy in the map.

    If not found, update the map with the string.
    """
    string = string or ""
    try:
        return strmap[string]
    except KeyError:
        strmap[string] = string
        return string

def get_rhythmbox_songs(dbfile, typ="song", keys=NEEDED_KEYS):
    """Return a list of info dictionaries for all songs
    in a Rhythmbox library database file, with dictionary
    keys as given in @keys.
    """
    rhythmbox_dbfile = os.path.expanduser(dbfile)

    lSongs = []
    strmap = {}

    # Parse with iterparse; we get the elements when
    # they are finished, and can remove them directly after use.

    for event, entry in ElementTree.iterparse(rhythmbox_dbfile):
        if not (entry.tag == ("entry") and entry.get("type") == typ):
            continue
        info = {}
        for child in entry.getchildren():
            if child.tag in keys:
                tag = _lookup_string(child.tag, strmap)
                text = _lookup_string(child.text, strmap)
                info[tag] = text
        lSongs.append(info)
        entry.clear()
    return lSongs
Run Code Online (Sandbox Code Playgroud)

现在,我不明白你的期望,你有以下期望吗?

# take one
for event, entry in ElementTree.iterparse(rhythmbox_dbfile):
    # parse some entries, then exit loop

# take two
for event, entry in ElementTree.iterparse(rhythmbox_dbfile):
    # parse the rest of entries
Run Code Online (Sandbox Code Playgroud)

每次调用iterparse时都会得到一个新的迭代器对象,重新读取文件!如果你想要一个带有迭代器语义的持久对象,你必须在两个循环中引用相同的对象(未尝试的代码):

#setup
parseiter = iter(ElementTree.iterparse(rhythmbox_dbfile))
# take one
for event, entry in parseiter:
    # parse some entries, then exit loop

# take two
for event, entry in parseiter:
    # parse the rest of entries
Run Code Online (Sandbox Code Playgroud)

我认为它可能会令人困惑,因为不同的对象具有不同的语义.文件对象将始终具有内部状态并在文件中前进,但是您将对其进行迭代.ElementTree iterparse对象显然不是.关键是要考虑当你使用for循环时,for总是在你迭代的东西上调用iter().这是一个将ElementTree.iterparse与文件对象进行比较的实验:

>>> import xml.etree.cElementTree as ElementTree
>>> pth = "/home/ulrik/.local/share/rhythmbox/rhythmdb.xml"
>>> iterparse = ElementTree.iterparse(pth)
>>> iterparse
<iterparse object at 0x483a0890>
>>> iter(iterparse)
<generator object at 0x483a2f08>
>>> iter(iterparse)
<generator object at 0x483a6468>
>>> f = open(pth, "r")
>>> f
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98>
>>> iter(f)
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98>
>>> iter(f)
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98>
Run Code Online (Sandbox Code Playgroud)

你看到的是在iterparse对象上每次调用iter()都会返回一个新的生成器.但是,文件对象具有必须保留的内部操作系统状态,并且它具有自己的迭代器.


Luk*_*ský 6

"即时"解析和文档树并不真正兼容.通常使用SAX样式的解析器(例如,Python的标准xml.sax).您基本上必须为各种事件(如startElement,endElement等)定义一个带有处理程序的类,解析器将在解析XML文件时调用这些方法.


Ric*_*dle 5

PullDom做你想做的事。它从流中读取 XML,比如 SAX,然后为它的选定部分构建一个 DOM。

“PullDOM 是一个非常简单的 API,用于以流(高效!)的方式处理 DOM 对象,而不是作为单一的树。”