2010-09-25 70 views
1

我有一個XML文件,其中包含100個文檔。每個塊如下所示:Python中的XML解析幫助

<DOC> 
<DOCNO> FR940104-2-00001 </DOCNO> 
<PARENT> FR940104-2-00001 </PARENT> 
<TEXT> 

<!-- PJG FTAG 4703 --> 

<!-- PJG STAG 4703 --> 

<!-- PJG ITAG l=90 g=1 f=1 --> 

<!-- PJG /ITAG --> 

<!-- PJG ITAG l=90 g=1 f=4 --> 
Federal Register 
<!-- PJG /ITAG --> 

<!-- PJG ITAG l=90 g=1 f=1 --> 
/Vol. 59, No. 2/Tuesday, January 4, 1994/Notices 
<!-- PJG 0012 frnewline --> 

<!-- PJG /ITAG --> 

<!-- PJG ITAG l=01 g=1 f=1 --> 
Vol. 59, No. 2 
<!-- PJG 0012 frnewline --> 

<!-- PJG /ITAG --> 

<!-- PJG ITAG l=02 g=1 f=1 --> 
Tuesday, January 4, 1994 
<!-- PJG 0012 frnewline --> 

<!-- PJG 0012 frnewline --> 

<!-- PJG /ITAG --> 

<!-- PJG /STAG --> 

<!-- PJG /FTAG --> 
</TEXT> 
</DOC> 

我想將此XML文檔加載到字典Text。鍵爲DOCNO &作爲文本在標籤內的值。此外,這個文本不應該包含所有的評論。示例Text['FR940104-2-00001']必須包含Federal Register/Vol. 59, No. 2/Tuesday, January 4, 1994/Notices Vol. 59, No. 2 Tuesday, January 4, 1994。這是我寫的代碼。

L = doc.getElementsByTagName("DOCNO") 
for node2 in L: 
    for node3 in node2.childNodes: 
     if node3.nodeType == Node.TEXT_NODE:    
      docno.append(node3.data); 
     #print node2.data 
L = doc.getElementsByTagName("TEXT") 
i = 0 
for node2 in L: 
    for node3 in node2.childNodes: 
     if node3.nodeType == Node.TEXT_NODE: 
      Text[docno[i]] = node3.data 
    i = i+1 

出人意料的是,我的代碼我得到的文本[「FR940104-2-00001」]作爲u'\n'怎麼來的?如何得到我想要的東西

+0

你的問題不是很清楚 – t00ny 2010-09-25 23:36:20

+0

@ t00ny:改進了我的問題。 – pecker 2010-09-25 23:42:05

回答

0

你行

Text[docno[i]] = node3.data 

取代的映射值,而不是追加新的。您的<TEXT>節點既有文本又有註釋的子節點,彼此交錯。

0

DOM解析器會自動去除註釋。每一行都是一個節點。

所以,你需要使用:

Text[docno[i]]+= node3.data但你需要有所有鍵一個空的字典來之前。因此,您可以在您的第一個代碼塊中添加Text[node3.data] = '';

所以,你的代碼就變成了:

L = doc.getElementsByTagName("DOCNO") 
for node2 in L: 
    for node3 in node2.childNodes: 
     if node3.nodeType == Node.TEXT_NODE:    
      docno.append(node3.data); 
      Text[node3.data] = ''; 
     #print node2.data 

L = doc.getElementsByTagName("TEXT") 
i = 0 
for node2 in L: 
    for node3 in node2.childNodes: 
     if node3.nodeType == Node.TEXT_NODE: 
      Text[docno[i]]+= node3.data 
    i = i+1 
4

你可能避免通過文檔循環兩次通過xml.sax.handler

import xml.sax.handler 
import collections 


class DocBuilder(xml.sax.handler.ContentHandler): 
    def __init__(self): 
     self.state='' 
     self.docno='' 
     self.text=collections.defaultdict(list) 
    def startElement(self, name, attrs): 
     self.state=name 
    def endElement(self, name): 
     if name==u'TEXT': 
      self.docno='' 
    def characters(self,content):   
     content=content.strip() 
     if content: 
      if self.state==u'DOCNO': 
       self.docno+=content 
      elif self.state==u'TEXT': 
       if content: 
        self.text[self.docno].append(content) 


with open('test.xml') as f: 
    data=f.read()    
builder = DocBuilder() 
xml.sax.parseString(data, builder) 
for key,value in builder.text.iteritems(): 
    print('{k}: {v}'.format(k=key,v=' '.join(value))) 
# FR940104-2-00001: Federal Register/Vol. 59, No. 2/Tuesday, January 4, 1994/Notices Vol. 59, No. 2 Tuesday, January 4, 1994 
+0

我們可以使用lxml作爲SAX解析器,或者lxml.sax與xml.sax有什麼不同? – shahjapan 2010-09-27 13:21:15

+1

@滾石:是的,你可以使用lxml.sax.saxify代替。語法幾乎和xml.sax完全一樣,不過您必須將'startElement'更改爲'startElementNS',因爲lxml.sax僅支持名稱空間感知處理。請參閱http://codespeak.net/lxml/sax.html – unutbu 2010-09-27 19:05:23

+1

@Tumbleweed:另一種選擇是使用'lxml.etree.iterparse'或'lxml。etree.XMLParser'與自定義目標。查看Liza Daly的優秀文章http://www.ibm.com/developerworks/xml/library/x-hiperfparse/#ibm-pcon,瞭解如何快速迭代解析而無需在內存中構建完整的分析樹。 – unutbu 2010-09-27 19:17:24

1

使用lxml

import lxml.etree as le 
with open('test.xml') as f: 
    doc=le.parse(f) 

texts={} 
for docno in doc.xpath('DOCNO'): 
    docno_text=docno.text.strip()  
    text=' '.join([t.strip() 
      for t in docno.xpath('following-sibling::TEXT[1]/text()') 
      if t.strip()]) 
    texts[docno.text]=text 

print(texts) 
# {'FR940104-2-00001': 'Federal Register/Vol. 59, No. 2/Tuesday, January 4, 1994/Notices Vol. 59, No. 2 Tuesday, January 4, 1994'} 

這個版本是一個比我的第一個lxml解決方案簡單。 它處理DOCNO,TEXT節點的多個實例。 DOCNO/TEXT節點應該交替,但在任何情況下,DOCNO都與其後面最近的TEXT節點相關聯。

2

到unutbu的答案相似,但我認爲簡單:

from lxml import etree 
with open('test.xml') as f: 
    doc=etree.parse(f) 

result={} 
for elm in doc.xpath("/DOC[DOCNO]"): 
    key = elm.xpath("DOCNO")[0].text.strip() 
    value = "".join(t.strip() for t in elm.xpath("TEXT/text()") if t.strip()) 
    result[key] = value 

需要改變的是找到DOC元素在這個例子中的XPath是適合您的真正的文件 - 例如如果有一個頂級元素(所有DOC元素都是子元素),則會將其更改爲/*/DOC。該XPath謂詞跳過沒有DOCNO子元素的任何DOC元素,否則在設置密鑰時會導致異常。

+0

感謝您的支持。我認爲你的版本不僅更簡單,而且(不像我現在刪除的基於lxml的答案)能夠正確處理相鄰的'DOCNO',而且它們之間沒有'TEXT'。 – unutbu 2010-09-26 19:55:50

+0

+1 [lxml](http://codespeak.net/lxml)。比標準庫中的python的xml支持好得多。 – snapshoe 2010-09-26 20:19:08

+0

@unutbu:它實際上根本不處理相鄰的'DOCNO's。它找到至少有一個「DOCNO」子元素的「DOC」元素。對於每一個,它都會在第一個'DOCNO'元素中查找關鍵字。如果有多個'DOCNO',它會忽略除第一個以外的所有內容。另外,如果有多個「TEXT」子節點,它將文本節點連接在一起。 – 2010-09-26 21:55:20