DOM說明:
DOM:Document Object Model API
DOM是一種跨語言的XML解析機制,DOM把整個XML檔案或字串在記憶體中解析為樹型結構方便訪問。
xml.dom.minidom就是DOM在Python中實現,本文主要結合minidom解釋DOM架構。
API匯入:
from xml.dom.minidom import parse from xml.dom.minidom import parseString import xml.dom.minidom dom和etree是xml package目錄下的兩個subpackage,minidom和ElementTree是dom和etree下的兩個module檔案,以.py字尾,其中定義了一系列的類和方法。 Document.documentElement相當於Etree中的tree.getroot()用於獲取整個樹唯一的根節點
概念解析:
xml.dom中包含以下類:
1.DOMImplementation 2.Node Node是最重要的類,XML被解析為一個樹,所有的節點都是都是node的子類,這些節點可以是element、comments等等,官網列出的節點型別就有: ELEMENT_NODE, ATTRIBUTE_NODE, TEXT_NODE, CDATA_SECTION_NODE, ENTITY_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE,DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE,每個節點有一個數字表示,只要是node型別就可以使用node.nodeType來判斷他屬於哪一種node,例如if child.nodeType==child.ELEMENT_NODE:或者if child.nodeType==1: --兩者等價 3.NodeList --通過getElementsByTagName()方法返回的nodelist,此方法只有element和document兩個類有。 4.DocumentType 5.Document --整個XML檔案解析樹,包含所有element、attribute、comments、text等等,也是node的子類。 6.Element 7.Attr --element的屬性,這個型別的node只能由Element.getAttributeNode(attrname)來獲取,無法遍歷獲取,而且其既不是element node的子節點也不是兄弟節點。幾乎從無必要獲取此節點,直接使用element類的getAttribute(attrname)來得到屬性的值即可。 8.Comment --comment節點,表示XML檔案註釋節點 9.Text --xml.etree.ElementTree中的text表示的是element中的內容,而這裡的text型別表示一個node,這個node可以是element中的data節點也可以是element之間的換行和製表符( ),如果是element的data內容那麼此text是element的唯一子節點,通過childNodes[0].data或firstChild.data獲取element內容,如果是換行製表符那麼此節點element的兄弟節點。 10.ProcessingInstruction 除node類之外,對於XML解析最重要的就是Document類和Element、text、Comment、Attr等類,前者配合parse()或parseString()將xml檔案或字串在記憶體中例項化為一個tree(document型別),後邊的類用於對XML樹做各種操作和查詢。
鑑於幾乎所有的可操作物件類都是繼承於node類,這裡貼一下node的各種屬性和方法的連結:
另外再列出node一些常見的屬性和方法: Node.nodeType --詳見上邊對Node類的解釋 Node.attributes --只有element型別的node才有此屬性 Node.childNodes --返回節點的子節點nodelist,與通過getElementsByTagName()獲取nodelist的區別在於此方法只返回直接子節點而非全部子節點,此外這兩個方法的最大區別是:childNodes返回的是所有子節點的集合,而getElementsByTagName(tagName)必須指定tagName。 Node.previousSibling --node的左兄弟節點,如果沒有則返回none Node.nextSibling --node的右兄弟節點,如果沒有則返回none Node.nodeName --不常用,因為繼承於node的各種類都有自己的更便於識別的name屬性,例如element.tagName Node.appendChild(newChild)
另:如果要熟練的使用minidom API,那麼請務必將https://docs.python.org/2/library/xml.dom.html 熟讀,以上列出的各種繼承於node的類都有一些自己獨特的屬性和方法,除了熟悉node類之外,熟悉這些繼承子類的方法也是很有必要的。
XML檔案解析示例:
--有一個如下的XML檔案:proxool.xml: <?xml version="1.0" encoding="utf-8"?> <something-else-entirely> <proxool> <alias>myPool</alias> <!-- mysql 連線配置,注意修改database_hostname為相應的資料庫主機名、或IP地址 --> <driver-url> jdbc:mysql://dbsrv:3306/TEST?useUnicode=true&characterEncoding=UTF8 </driver-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <!-- 使用者名稱、密碼 --> <driver-properties> <property name="user" value="leo" /> <property name="password" value="leo" /> </driver-properties> <!--自動偵察各個連線狀態的時間間隔(毫秒),偵察到空閒的連線就馬上回收,超時的銷燬 --> <house-keeping-sleep-time>30000</house-keeping-sleep-time> <house-keeping-test-sql>select CURRENT_DATE from dual </house-keeping-test-sql> <!--最大連線數(預設5個),超過了這個連線數,再有請求時,就排在佇列中等候,最大的等待請求數由maximum-new-connections決定 --> <maximum-connection-count>120</maximum-connection-count> <!--最小連線數(預設2個) --> <minimum-connection-count>5</minimum-connection-count> <!--沒有空閒連線可以分配而在佇列中等候的最大請求數,超過這個請求數的使用者連線就不會被接受,該引數已經不建議使用,由simultaneous-build-throttle替代 --> <!--一個活動連線最大活動時間預設5分鐘 --> <maximum-active-time>3600000</maximum-active-time> <!--最少保持的空閒連線數(預設2個),如果當前的連線池中的可用連線少於這個數值, 新的連線將被建立 --> <prototype-count>5</prototype-count> <!--可一次建立的最大連線數 --> <simultaneous-build-throttle>20</simultaneous-build-throttle> <!--如果為true,那麼每個被執行的SQL語句將會在執行期被log記錄 --> <trace>false</trace> </proxool> </something-else-entirely>
現在將其中的內容解析為如下格式:
***** 描述:最大連線數(預設5個),超過了這個連線數,再有請求時,就排在佇列中等候,最大的等待請求數由maximum-new-connections決定 配置項:maximum-connection-count 配置值:120 ***** 描述:xxx 配置項:xxx 配置值:xxx ***** ......
程式碼如下:
# -*- coding:utf-8 -*- # 本指令碼適用於Python2和3 from xml.dom.minidom import parse import xml.dom.minidom import sys # file = sys.argv[1] file = "/root/proxool.xml" # 先寫一個判斷節點是否包含element型別子節點的判斷函式 def has_element_child(nodename): has_element_child = 0 for child in nodename.childNodes: if child.nodeType==1: has_element_child += 1 return has_element_child # 定義解析示例XML檔案的方法 def parse_xml(file): if not file: sys.exit(0) tree = parse(file) # document型別的解析樹 root = tree.getElementsByTagName(`proxool`)[0] # 將父節點定位到proxool element for child in root.childNodes: if child.nodeType==child.ELEMENT_NODE and has_element_child(child)==0: # 當node為element型別,且無element型別的子節點時 print u`配置項`+": %s" % child.tagName print u`配置值`+": %s" % child.firstChild.data.strip() elif child.nodeType==child.ELEMENT_NODE and has_element_child(child)>0: # 當節點包含element型別子節點時 for child_child in child.childNodes: if child_child.nodeType==child.ELEMENT_NODE: print u`配置項`+": %s" % child_child.getAttribute(`name`) print u`配置值`+": %s" % child_child.getAttribute(`value`) elif child.nodeType==child.COMMENT_NODE: # 當node為comment型別時 print "*****" print u`描述`+": %s" % child.data else: pass # 處理示例XML檔案 parse_xml(file)
XML檔案比較修改示例:
minidom相比於DOM API最大的差別就是新增了node.writexml()、node.toprettyxml()等方法,這兩個方法可以將你對XML解析樹作出的修改寫入檔案中,現在我們將proxool.xml copy到proxool.xml.new中,並在proxool節點下新增一個子節點<new_tag name=”Leo”>For_Test</new_tag>,我們要比較新XML檔案中比舊XML檔案新增的配置項,對舊XML的配置項不做修改,程式碼如下:
# -*- coding:utf-8 -*- # 本指令碼適用於Python2和3 from xml.dom.minidom import parse import xml.dom.minidom import sys reload(sys) sys.setdefaultencoding("utf-8") old_file = sys.argv[1] new_file = sys.argv[2] # 先寫一個判斷節點是否包含element型別子節點的判斷函式 def has_element_child(nodename): has_element_child = 0 for child in nodename.childNodes: if child.nodeType==1: has_element_child += 1 return has_element_child # 定義解析示例XML檔案的方法 def match_xml(old_file,new_file): if not new_file: sys.exit(0) tree_old = parse(old_file) # document型別的解析樹 tree_new = parse(new_file) root_old = tree_old.getElementsByTagName(`proxool`)[0] # 將父節點定位到proxool root_new = tree_new.getElementsByTagName(`proxool`)[0] old_dict = {} # 定義舊XML檔案的tag和data的字典 new_dict = {} for child in root_old.childNodes: #將tagName和data存入old_dict{}中 if child.nodeType==child.ELEMENT_NODE and has_element_child(child)==0: # 當node為element型別,且無element型別的子節點時 old_dict[child.tagName] = child.firstChild.data.replace(" ", "").replace(" ", "") for child in root_new.childNodes: if child.nodeType==child.ELEMENT_NODE and has_element_child(child)==0: new_dict[child.tagName] = child.firstChild.data.replace(" ", "").replace(" ", "") for tag,data in new_dict.items(): if not old_dict.get(tag): # 當舊XML中找不到對應的tag時,進行tag新增操作 new_element=tree_new.getElementsByTagName(tag) for child in new_element: root_old.appendChild(child) # 新增element節點 with open(`proxool_modified.xml`,`w`) as f: tree_old.writexml(f) f.close # 處理示例XML檔案 match_xml(old_file,new_file)
--比較XML檔案: # python xml_match_dom.py proxool.xml proxool.xml.new --然後就可以在proxool_modified.xml中看到新的XML內容了