如何从Python SAX解析器返回数据?

Fre*_*Foo 5 python xml sax

我一直在尝试解析LXML不会理解的一些巨大的XML文件,所以我不得不用它来解析它们xml.sax.

class SpamExtractor(sax.ContentHandler):
    def startElement(self, name, attrs):
        if name == "spam":
            print("We found a spam!")
            # now what?
Run Code Online (Sandbox Code Playgroud)

问题是我不明白如何实际return或更好地yield处理这个处理程序找到调用者的事情,而不等待整个文件被解析.到目前为止,我一直在瞎搞与threading.ThreadQueue.Queue,但是这会导致各种问题与线程真的分散我的注意力从实际的问题,我试图解决的问题.

我知道我可以在一个单独的进程中运行SAX解析器,但我觉得必须有一种更简单的方法来获取数据.在那儿?

Gar*_*tty 6

我以为我会把它作为另一个答案,因为它是一种完全不同的方法.

你可能想看看xml.etree.ElementTree.iterparse它似乎做了你想做的更多:

将XML部分逐步解析为元素树,并报告用户正在进行的操作.source是包含XML数据的文件名或文件对象.events是要报告的事件列表.如果省略,则仅报告"结束"事件.parser是一个可选的解析器实例.如果没有给出,则使用标准XMLParser解析器.返回提供(event,elem)对的迭代器.

然后,您可以编写一个生成器,使用该迭代器,执行您想要的操作,并生成所需的值.

例如:

def find_spam(xml):
    for event, element in xml.etree.ElementTree.iterparse(xml):
        if element.tag == "spam":
            print("We found a spam!")
            # Potentially do something
            yield element
Run Code Online (Sandbox Code Playgroud)

差异很大程度上取决于你想要的.ElementTree的迭代器方法更多地是关于收集数据,而SAX方法更多的是关于它的行为.

  • +1但我要添加以下内容:(1)使用`cElementTree`,而不是`ElementTree`(2)`lxml`也有一个`iterparse`,提供相同或更好的功能(3)你需要提到删除节点在您提取所需信息后(4)AFAICT(从未尝试过),生成器应该可以正常工作 (2认同)

unu*_*tbu 5

David Beazley演示了如何使用协程从sax ContentHandler"产生"结果:

cosax.py:

import xml.sax

class EventHandler(xml.sax.ContentHandler):
    def __init__(self,target):
        self.target = target
    def startElement(self,name,attrs):
        self.target.send(('start',(name,attrs._attrs)))
    def characters(self,text):
        self.target.send(('text',text))
    def endElement(self,name):
        self.target.send(('end',name))

def coroutine(func):
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        cr.next()
        return cr
    return start

# example use
if __name__ == '__main__':
    @coroutine
    def printer():
        while True:
            event = (yield)
            print event

    xml.sax.parse("allroutes.xml",
                  EventHandler(printer()))
Run Code Online (Sandbox Code Playgroud)

上面,每次self.target.send调用,内部代码printer从中开始运行event = (yield).event被分配给参数self.target.send,并且执行代码printer直到(yield)达到下一个,类似于生成器的工作方式.

而发电机通常由a驱动for-loop,而协程(例如printer)由send呼叫驱动.