我一直在尝试解析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.Thread和Queue.Queue,但是这会导致各种问题与线程真的分散我的注意力从实际的问题,我试图解决的问题.
我知道我可以在一个单独的进程中运行SAX解析器,但我觉得必须有一种更简单的方法来获取数据.在那儿?
我以为我会把它作为另一个答案,因为它是一种完全不同的方法.
你可能想看看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方法更多的是关于它的行为.
David Beazley演示了如何使用协程从sax ContentHandler"产生"结果:
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呼叫驱动.