2014-07-25 37 views
4

我从服务器接收XML块。这些块是不完整的片段,但可以看看比如像这样:Python:解析不完整的XML块

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

然而,这种方法并没有真正的工作,因为它仅调用handle_elementtext包含事故格式良好的XML文档,但不能保证这将永远如此。

+1

如果你想增量XML解析,你使用了错误的模块......你需要'xml.sax'。附加到一个简单的文件类型的对象,从另一端缓冲数据,我想你会得到你想要的。 'etree'和其他DOM类型的解析器希望一次加载整个文件并以原子方式处理它。或尝试BeautifulSoup,还没有尝试过,但认为它应该处理这些情况。 –

+0

好的,谢谢,我看看那两个。但要明确,我不能访问“另一端”。我只是得到那些字符串xml片断,这就是我所拥有的。 – basilikum

+0

那些是非常小的块。你可以将套接字连接的缓冲区大小设置为(可能)允许一次接收整个消息吗? –

回答

2

也许你可以使用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方法的对象。因此,如果您创建一个读取方法从服务器读取的对象,那么您可以将其挂接到ET.iterparse以执行增量解析。

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

+0

谢谢。这是一个很好的答案。我想我需要做一些更正确的实施,但一些第一次测试显示,它绝对做我想要的:) – basilikum

0

您正在尝试制作一个XML对象,然后您有适当的XML Sting(我相信您已经想通了)。基本上,您将所有字符串/块连接在一起,一旦拥有完整的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''' 

希望帮助,我们做的工作非常类似的东西,但我不记得我们究竟如何来实现它。

+0

谢谢,但不幸的是,这不适合我。我想要在一个元素完成时触发一个函数,而不仅仅是当整个文档完成时。此外,没有完整的文件。我只收到单个xml文件。 – basilikum

+0

如果您正在寻找单独解析每个元素,则仍然必须应用相同的缓冲区方法(某些形状或形式)。 xml.sax可能会有所帮助,但我从未使用过 –