Python中非阻塞的XML解析方法(流式)
我有一个XML文档是通过网络套接字传过来的,我需要实时解析它并做出反应(也就是说,解析一个部分的树)。我希望能有一种非阻塞的方法,这样我可以在等待更多数据到来的时候做其他事情(而不使用线程)。
像iterparse这样的方式就很理想,如果它在读取缓冲区为空时能结束迭代,比如:
context = iterparse(imaginary_socket_file_wrapper)
while 1:
for event, elem in context:
process_elem(elem)
# iteration of context finishes when socket has no more data
do_other_stuff()
time.sleep(0.1)
我想SAX也是一个选择,但对我来说,iterparse似乎更简单。有没有什么好的建议?
更新:
使用线程是可以的,但会增加一些复杂性,我本来希望能避免这些。我觉得非阻塞调用是个不错的办法,但我发现这反而让解析XML变得更复杂了。
3 个回答
如果你不打算使用线程,可以考虑使用事件循环和非阻塞的套接字。
asyncore
是处理这类事情的标准库模块。Twisted 是 Python 中非常流行的异步库,但它比较复杂,可能对你的需求来说有点重。
另外,multiprocessing
是一种不使用线程的替代方案,但我猜你可能不是在用 2.6 版本。
无论如何,我觉得你可能还是需要使用线程、额外的进程,或者编写一些同样复杂的异步代码。
我觉得这个问题有两个部分,一个是非阻塞的网络输入输出,另一个是面向流的XML解析器。
对于第一个部分,你需要选择一个非阻塞的网络框架,或者自己想办法解决这个问题。Twisted这个框架当然可以用,但我个人觉得控制反转的框架有点难以理解。你可能需要在回调函数中跟踪很多状态,以便给解析器提供数据。因此,我觉得Eventlet编程起来稍微简单一些,我认为它在这种情况下会很合适。
简单来说,它让你可以像使用阻塞的套接字调用一样写代码(用普通的循环、生成器或者你喜欢的方式),不过你可以把它放到一个单独的协程(叫做“greenlet”)中,这样在输入输出操作会阻塞的时候,它会自动让出控制权,让其他协程可以运行。
这使得使用任何面向流的解析器变得非常简单,因为代码的结构就像普通的阻塞调用一样。这也意味着很多不直接处理套接字或其他输入输出的库(比如解析器)不需要特别修改成非阻塞的:如果它们阻塞了,Eventlet会让出协程。
确实,Eventlet有点“魔法”,但我觉得它的学习曲线比Twisted要简单得多,写出来的代码也更直接,因为你不需要把逻辑“翻转”来适应这个框架。
我查看了iterparse的源代码,找到了问题的解决办法。下面是一个简单的例子,展示了如何动态构建一个XML树,并在元素的结束标签后进行处理:
import xml.etree.ElementTree as etree
parser = etree.XMLTreeBuilder()
def end_tag_event(tag):
node = self.parser._end(tag)
print node
parser._parser.EndElementHandler = end_tag_event
def data_received(data):
parser.feed(data)
在我的情况下,我是用twisted提供的数据,但它也应该可以在非阻塞的socket上工作。