【问题标题】:Python: parsing incomplete XML chunksPython:解析不完整的 XML 块
【发布时间】:2014-07-25 13:46:54
【问题描述】:

我从服务器接收 XML 块。这些块不是完整的片段,但可能看起来像这样:

chunk1 = '<el a="1" b='
chunk2 = '"2"><sub c="'
chunk3 = '3">test</sub'
chunk4 = '></el><el d='
chunk5 = '"4" e="5"></'
chunk6 = 'el>'

如何解析这个流,以便每当一个“el”元素完成时调用一个函数?

到目前为止,我正在采用这种方法(使用 ElementTree):

import xml.etree.ElementTree as ET

text = ""

def handle_message(msg):
    text += msg
    try:
        root = ET.fromstring("<root>" + text + "</root>")
        for el in list(root):
            handle_element(el)
        text = ""
        return True
    except ET.ParseError:
        return False

但是,这种方法实际上并不奏效,因为它仅在 text 意外包含格式良好的 XML 文档时调用 handle_element,但不能保证永远都是这种情况。

【问题讨论】:

  • 如果你想要增量 XML 解析,你使用了错误的模块......你想要xml.sax。将它附加到一个简单的文件类型对象,该对象缓冲来自另一端的数据,我想你会得到你想要的。 etree 和其他 DOM 类型的解析器希望一次加载整个文件并以原子方式处理它。或者试试 BeautifulSoup,没试过,但认为它应该可以处理这些情况。
  • 好的,谢谢,我看看那两个。但为了清楚起见,我无法访问“另一端”。我只是得到那些字符串 xml 片段,这就是我所拥有的。
  • 那些是非常小的块。您能否将套接字连接的缓冲区大小提高到(也许)允许一次接收整个消息?
  • @notorious 这只是一个例子,实际上它们更大。但是不,我无法保证立即传输完整的元素。我也不能保证,如果一次传输一个元素,那么在该元素之后该块不包含任何额外的和不完整的内容。

标签: python xml xml-parsing


【解决方案1】:

您也许可以使用ET.iterparse 来增量解析 XML 块:

import xml.etree.ElementTree as ET

chunks = iter([
    '<root>'
    '<el a="1" b=',
    '"2"><sub c="',
    '3">test</sub',
    '></el><el d=',
    '"4" e="5"></',
    'el>',
    '</root>'
    ])


class Source(object):
    def read(self, size):
        # Replace this with code that reads XML chunks from the server
        return next(chunks)

for event, elem in ET.iterparse(Source(), events=('end', )):
    if elem.tag == 'el':
        print(elem)
        # handle_element(elem)

产量

<Element 'el' at 0xb744f6cc>
<Element 'el' at 0xb744f84c>

ET.iterparse 的第一个参数通常是一个文件名,或者一个 io.BytesIO 或 StringIO 对象。然而,它可以是具有read 方法的任何对象。因此,如果您创建一个对象,其 read 方法从服务器读取,那么您可以将其挂钩到 ET.iterparse 进行增量解析。

请注意,ET.iterparse 将使用请求的字节数调用 read 方法(例如 read(16384))。如果这是服务器给你的全部,你可以返回更少的字节,但我不确定如果你返回的字节数超过请求的字节数,是否会发生任何不好的事情。理想情况下,您应该能够将请求的字节数传递给服务器,并依靠服务器提供正确的字节数(或更少)。

【讨论】:

  • 谢谢。这是一个很好的答案。我认为我需要进行更多修改才能正确实现这一点,但一些初步测试表明,它确实符合我的要求:)
【解决方案2】:

您正在尝试在获得适当的 XML 字符串之前创建一个 XML 对象(我相信您已经弄清楚了)。基本上,您已经将所有字符串/块连接(连接)在一起,一旦您拥有完整的 XML,就可以使用完整的字符串创建一个 XML 对象。使用 io.BytesIO 或 io.StringIO ,每当您从服务器获取某些内容时,将其写入缓冲区,然后解析缓冲区并取出您需要的内容。

扭曲的例子:

from io import StringIO

def __init__(self):
    self.buffer = StringIO()    # Buffer obj

def dataReceived(self, data):
    # this is data that is received from the server
    self.buffer.write( data )    # Usually want this in a callBack

def processBuffer(self):
    string = self.buffer.getvalue()
    ''' Do your parsing 
        Then once you have the complete xml
        do etree.fromstring( string ) or equivalant'''

希望对您有所帮助,我们在工作中做了一些非常相似的事情,但我不记得我们是如何实现的。

【讨论】:

  • 谢谢,但不幸的是,这对我不起作用。我想在单个元素完成时触发一个函数,而不仅仅是在整个文档完成时。此外,没有完整的文档。我只收到单个 xml 片段。
  • 如果您希望单独解析每个元素,那么仍然必须应用相同的缓冲方法(以某种形式或形式)。 xml.sax 可能会有所帮助,但我从未使用过它
猜你喜欢
  • 1970-01-01
  • 2014-01-16
  • 2018-01-09
  • 1970-01-01
  • 1970-01-01
  • 2011-04-22
  • 2012-10-28
  • 2019-12-03
  • 2012-04-08
相关资源
最近更新 更多