2012-10-09 63 views
18

我必須處理足夠大(最大支持1GB),XML文檔和使用python解析。我正在使用iterparse()函數(SAX樣式解析)。ElementTree的iterparse戰略

我關心的是下面,想象你有這樣

<?xml version="1.0" encoding="UTF-8" ?> 
<families> 
    <family> 
    <name>Simpson</name> 
    <members> 
     <name>Homer</name> 
     <name>Marge</name> 
     <name>Bart</name> 
    </members> 
    </family> 
    <family> 
    <name>Griffin</name> 
    <members> 
     <name>Peter</name> 
     <name>Brian</name> 
     <name>Meg</name> 
    </members> 
    </family> 
</families> 

的問題當然是,一個xml知道什麼時候我得到一個姓氏(如辛普森),當我得到的名字的家庭成員之一(例如荷馬)

我到目前爲止一直在做的事情是使用「開關」,它會告訴我,如果我在「成員」標籤內或不是,代碼將如下所示

import xml.etree.cElementTree as ET 

__author__ = 'moriano' 

file_path = "test.xml" 
context = ET.iterparse(file_path, events=("start", "end")) 

# turn it into an iterator 
context = iter(context) 
on_members_tag = False 
for event, elem in context: 
    tag = elem.tag 
    value = elem.text 
    if value : 
     value = value.encode('utf-8').strip() 

    if event == 'start' : 
     if tag == "members" : 
      on_members_tag = True 

     elif tag == 'name' : 
      if on_members_tag : 
       print "The member of the family is %s" % value 
      else : 
       print "The family is %s " % value 

    if event == 'end' and tag =='members' : 
     on_members_tag = False 
    elem.clear() 

而作爲輸出

The family is Simpson 
The member of the family is Homer 
The member of the family is Marge 
The member of the family is Bart 
The family is Griffin 
The member of the family is Peter 
The member of the family is Brian 
The member of the family is Meg 

我擔心的是這個(簡單的)例子我不得不創建一個額外的變量知道在哪個標籤,我是(on_members_tag)與真正的XML實例想象這工作正常我必須處理,他們有更多的嵌套標籤。

另外請注意,這是一個非常減少的例子,所以你可以認爲我可能會面臨一個xml更多標籤,更多的內部標籤,並試圖讓不同的標記名稱,屬性等。

所以問題是。我在這裏做了一件非常愚蠢的事情嗎?我覺得必須有一個更優雅的解決方案。

+0

你會用數據做什麼?構建一個Python數據結構來保存它,或者在迭代時存儲在數據庫中,或者其他的東西? –

+0

@JanneKarila:數據可以在蟒蛇結構放,保存到數據庫或轉儲到一個文件中,這將取決於proccess,在這種情況下,你可以認爲它會被寫入到數據庫 –

回答

24

這裏是一個可能的辦法:我們維持一個路徑列表偷看往回走,找到父節點(一個或多個)。

path = [] 
for event, elem in ET.iterparse(file_path, events=("start", "end")): 
    if event == 'start': 
     path.append(elem.tag) 
    elif event == 'end': 
     # process the tag 
     if elem.tag == 'name': 
      if 'members' in path: 
       print 'member' 
      else: 
       print 'nonmember' 
     path.pop() 
+0

簡單,優雅,沒有工作。非常感謝:) –

+0

這種方法是否有任何標準名稱?我相信這種方法被用於許多這樣的問題。如果你能說出它的名字,我可以深入瞭解並理解這一點。 –

11

pulldom非常適合這個。你得到一個薩克斯流。您可以遍歷流,並且當您找到您感興趣的節點時,將該節點加載到dom片段中。

import xml.dom.pulldom as pulldom 
import xpath # from http://code.google.com/p/py-dom-xpath/ 

events = pulldom.parse('families.xml') 
for event, node in events: 
    if event == 'START_ELEMENT' and node.tagName=='family': 
     events.expandNode(node) # node now contains a dom fragment 
     family_name = xpath.findvalue('name', node) 
     members = xpath.findvalues('members/name', node) 
     print('family name: {0}, members: {1}'.format(family_name, members)) 

輸出:

family name: Simpson, members: [u'Hommer', u'Marge', u'Bart'] 
family name: Griffin, members: [u'Peter', u'Brian', u'Meg'] 
+0

這是一個非常好的解決方案,但是我不能給你它作爲一個公認的答案(我喜歡nneonneo的回答更好),但是,它肯定看起來像一個優雅的解決方案。謝謝! –

+0

很好的答案。使用非常簡單。允許解析一個46 GB的XML文件 –