在python中解析(流式传输)XML的非阻塞方法

Posted

技术标签:

【中文标题】在python中解析(流式传输)XML的非阻塞方法【英文标题】:Non-Blocking method for parsing (streaming) XML in python 【发布时间】:2009-09-22 11:59:05 【问题描述】:

我有一个通过套接字传入的 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 的复杂性。

【问题讨论】:

【参考方案1】:

深入研究 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 向它提供数据,但它也应该与非阻塞套接字一起使用。

【讨论】:

谢谢彼得。感谢您的回答,我终于找到了我的问题的另一个答案。查看我更详细的答案:***.com/a/44414167/938111【参考方案2】:

我认为这有两个组件,非阻塞网络 I/O 和面向流的 XML 解析器。

对于前者,您必须选择一个非阻塞网络框架,或者为此推出您自己的解决方案。 Twisted 肯定会起作用,但我个人发现控制框架的反转很难让我的大脑围绕。您可能必须跟踪回调中的许多状态才能为解析器提供数据。出于这个原因,我倾向于发现Eventlet 更容易编程,而且我认为它很适合这种情况。

基本上它允许您编写代码就好像您正在使用阻塞套接字调用(使用普通循环或生成器或任何您喜欢的东西),除了您可以将它生成为单独的协程(一个“greenlet”),当 I/O 操作阻塞时会自动执行一个协作的 yield,从而允许其他协程运行。

这使得使用任何面向流的解析器再次变得微不足道,因为代码的结构类似于普通的阻塞调用。这也意味着许多不直接处理套接字或其他 I/O 的库(例如解析器)不必特别修改为非阻塞:如果它们阻塞,Eventlet 会产生协程。

诚然,Eventlet 有点神奇,但我发现它的学习曲线比 Twisted 容易得多,并且代码更简单,因为您不必将逻辑“由内而外”转换为适合框架。

【讨论】:

Twisted 确实很难理解,但一旦您了解了Deferreds,它的功能就非常强大。我对 eventlet 进行了简要介绍,它基于线程,OP 排除了这些线程。不过,没有说为什么线程不是一种选择。如果这是一个复杂的问题,Eventlet 看起来充满了胜利。 其实Eventlet并不需要线程,但它与线程是正交且兼容的。它使用了一个名为“greenlet”的 C 扩展,它实现了协作协程,可以将其视为 Python 生成器的通用版本:greenlet 可以产生控制权,并在稍后从它停止的地方恢复。 Eventlet 使用此功能在执行会阻塞的 I/O 操作时自动生成任何 greenlet,然后在 I/O 完成时恢复它。事实上,它在引擎盖下使用了一个类似 Twisted 的反应器,但它是安排 greenlets 而不是直接暴露它。 Twisted 似乎专注于 html 和 HTTP。有没有将 Twisted 与普通的 XML 和 TCP 套接字一起使用的示例? edarc:哦,对了,明白了。这与使用线程相比有什么优势? Greenlets 比操作系统线程轻得多。把它们想象成 Erlang 进程——它们是虚拟机的制造,而不是真正的操作系统线程,伴随着内存和上下文切换开销。因此,您可以获得非阻塞事件驱动模型的并发优势,以及线程模型的直接控制流。【参考方案3】:

如果您不使用线程,则可以使用事件循环并轮询非阻塞套接字。

asyncore 是此类东西的标准库模块。 Twisted 是 Python 的 异步库,但很复杂,可能有点重量级,以满足您的需求。

或者,multiprocessing 是非线程线程替代方案,但我假设您没有运行 2.6。

无论哪种方式,我认为您将不得不使用线程、额外的进程或编织一些同样复杂的异步魔法。

【讨论】:

asyncore 看起来不错,但我仍然需要一种解析 XML 的方法,它不会超过将 iterparse 放入另一个线程(比如)的复杂性 对。不知道为什么线程出来了。 Eventlets 看起来不错,但在这种情况下并不比线程简单得多。在我看来,如果您想要并发/异步/线程行为,您将不得不以一种或另一种方式为这种复杂性付出代价。 XML 库(或几乎任何其他库)不会附带这种功能,因为它在不同的抽象级别上做得更好。

以上是关于在python中解析(流式传输)XML的非阻塞方法的主要内容,如果未能解决你的问题,请参考以下文章

从 WebClient 流式传输到 Flux。阻塞超时抛出异常

基于多线程的非阻塞 socket 编程

基于select的非阻塞ftp传输(未优化)

基于select的非阻塞ftp传输(未优化)

如何在 PowerShell 中使用 XmlReader 流式传输大/巨大的 XML 文件?

使用 lxml.etree.iterparse 解析损坏的 XML