2014-07-25 64 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可能會有所幫助,但我從未使用過 –