python 處理xml 筆記


文檔模型:用以描述詞匯和文檔結構,定義文檔中將要出現的數據元素,元素之間的關系,以及元素的數量等

實現文檔模型的方法:模式 和 DTD (document type definition 文檔類型定義)

1 文檔模型的用途

文檔模型用於在處理文檔之前,驗證它的內容是否符合標准。

DTD是文檔類型定義,表示文檔模型的最原始的方法

在文件頂部聲明后插入一行:<!DOCTYPE config SYSTEM "library.dtd"> library.dtd是系統上的DTD路徑

DTD中元素頻率和元素分組操作符

?指定0個或者1個前面出現的元素。

+ 指定一個或者多個前面出現的元素

,指定一列元素必須 以此特定的順序出現 (title,author+)意味着書必須有一個標題,隨后是一個或者多個作者,必須以該順序出現

(list) 將元素組織在一起。應用於圓括號后的運算符適用於組中的所有元素。(author,editor)+的含義是一篇文檔可能有多個作者與多個編輯。

!或 運算符。該運算符允許在多個選項之間選擇 (author|editor)允許一本書有一個作者或者一個編輯,但是不能同時具有兩者

* 指定前面的元素或組出現0 次或多次。(book,CD)*允許圖書館中有任意數目的書或者CD 或者什么都沒有,是空的。

3 XPath 是在xml文檔中描述位置和節點集合的語言。xpath表達式包含對某個節點必須匹配的模式的描述。模式要么相對於一個上下文節點,要么由文檔的根節點絕對定義。絕對路徑以斜杠開始。路徑的每一步之間由斜杠分隔開。

路徑中的每一步包含3個部分:描述移動方向的軸,沿着該軸選擇節點的測試,還有可選的謂詞,它是節點必須滿足的boolean 型測試。

如:ancestor-or-self::book[1]其中ancestor-or-self是軸,book是節點測試,[1]是謂詞,指定選擇滿足所有其他條件的第一個節點。

節點測試既可以是一個函數,也可以是一個節點名稱 book/node()將返回選擇的書節點下的所有子節點,不管它們是文本還是元素。

@ 指定屬性軸,這是attribute::的縮寫

* 指定當前節點的所有子節點

//指定當前節點的所有后代節點 :descendant-or-self::*//的縮寫。如果在xpath開頭使用,它將匹配文檔中任意地方的元素。

4 html是xml 的子集 

為了解析一個html文檔,必須創建一個從HTMLParser繼承而來的類,並實現必要的方法

使用feed方法向解析器提供數據。可以每次提供一行數據,或者一次提供所有數據。

from html.parser import HTMLParser
class HeadingParser(HTMLParser):
inHeading=False
def handle_starttag(self,tag,attrs):#開始標簽
if tag=="h1":
self.inHeading=True
print("found a heading 1")
def handle_data(self,data):#處理標簽內容
if self.inHeading:
print(data)
def handle_endtag(self,tag): #結束標簽
if tag=="h1":
self.inHeading=False
hParser=HeadingParser()
file=open("headings.html","r")
html=file.read()
file.close()
hParser.feed(html)

5 在解析xml時,可以 選擇兩種不同類型的解析器:SAX和DOM

SAX代表XML的簡單API它是基於流的事件驅動的解析器。這些事件稱作 文檔事件,在元素開始之處、元素結尾處、遇到文本節點或者遇到一個注釋時都有可能發生。

當用SAX解析文檔時,文檔以期出現的順序被讀入和解析。解析器以數據流的方式打開該文件或者其他數據源如url,之后無論何時遇到任何元素都將引發事件。操作文檔不夠高效。對於文檔轉換,應該選擇SAX,因為事件驅動的模型速度很快。

DOM的核心在於文檔對象,它是XML文檔基於樹的表示形式。樹中的元素稱作節點對象,節點擁有屬性、子節點、文本 等,它們以對象的形式存儲在樹中。能在內存中存儲整個文檔,並且以樹的形式操作和搜索其中的元素。文檔大時預先處理時間長,處理后快。

python中解析器:xml.sax 和xml.dom.minidom

xml.dom.minidom 中parse方法解析文檔樹 返回一個Document對象。文本存儲於節點的data屬性

parse()函數可以引用一個文件名稱或一個打開的文件對象

from xml.dom.minidom import parse, parseString dom1 = parse('c:\\temp\\mydata.xml') # parse an XML file by name datasource = open('c:\\temp\\mydata.xml') dom2 = parse(datasource) # parse an open file dom3 = parseString('<myxml>Some data<empty/> some more data</myxml>') 

appendChild方法可以創建節點的結構

節點的方法 insertBefor(newChild,refChild)可以在節點的子節點列表中的任意位置插入新的子節點

方法replaceChild(newChild,oldChild)可以將一個子節點替換為別一個子節點

刪除節點 首先需要得到要刪除的節點的引用,隨后再調用 removeChild(childNode)方法。刪除后,調用unlink()方法強制對被刪除的節點及它可能連接的子節點進行垃圾回收,xml.dom中不可用。

xml.dom.minidom方法:toprettyxml,它接收兩個可選參數:一個是縮進字符串,一個是換行符。如果沒有指定參數值,這兩個參數分別默認為tabulator 和\n。該方法將DOM 打印為包含良好縮進的XML

 

<?xml version="1.0"?>
<lib:library
xmlns:lib="http://server.domain.tld/NameSpaces/Library">
<lib:book>
<lib:title>Sandman volumn</lib:title>
<lib:author>Neil Gaiman</lib:author>
</lib:book>
<lib:book>
<lib:title>Good omens</lib:title>
<lib:author>Neil Gamain</lib:author>
<lib:author>Terry Pratchett</lib:author>
</lib:book>
<lib:book>
<lib:title>"Repent,harlequin!" said the man</lib:title>
<lib:author>Harlan Ellison</lib:author>
</lib:book>
</lib:library>

以上的xml 保存在文件里去掉命名空間可以使用以下代碼操作

import os
from xml.dom.minidom import parse
import xml.dom.minidom
def printLibrary(library):
books=myLibrary.getElementsByTagName("book")
for book in books:
print("*******book******")
print("Title:%s"%book.getElementsByTagName("title")[0].childNodes[0].data)
for author in book.getElementsByTagName("author"):
print("author:%s"%author.childNodes[0].data)
# open an xml file and parse it into a dom
myDoc=parse(r'E:\pythonscript\ch15\library.xml')
myLibrary=myDoc.getElementsByTagName("library")[0]
#get all the book elements in the library
books=myLibrary.getElementsByTagName("book")

#Insert a new book in the library
newBook=myDoc.createElement("book")
newBookTitle=myDoc.createElement("title")
titleText=myDoc.createTextNode("Beginning Python")
newBookTitle.appendChild(titleText)
newBook.appendChild(newBookTitle)
newBookAuthor=myDoc.createElement("author")
authorName=myDoc.createTextNode("Peter Norton,et al")
newBookAuthor.appendChild(authorName)
newBook.appendChild(newBookAuthor)
myLibrary.appendChild(newBook)
print("--------added a new book!")
#printLibrary(myLibrary)
#remove a book from the library
#find ellison book
for book in myLibrary.getElementsByTagName("book"):
for author in book.getElementsByTagName("author"):
if author.childNodes[0].data.find("Ellison")>=0:
print(author.childNodes[0].data)
removedBook=myLibrary.removeChild(book)
removedBook.unlink()
print("------------removed a book.")
#printLibrary(myLibrary)
print(myDoc.toprettyxml())
#write back to the library file
lib=open(r"E:\pythonscript\ch15\library.xml","w")
lib.write(myDoc.toprettyxml(" "))
lib.close()# 這里是個方法,如果沒有 這個方法 則不能寫入數據,文件一直被占用

使用sax 解析:

#!/usr/bin/python
from xml.sax import make_parser
from xml.sax.handler import ContentHandler
#begin bookHandler
class bookHandler(ContentHandler):
inAuthor=False
inTitle=False
def startElement(self,name,attributes):
if name=="book":
print("********book*********")
if name=="title":
self.inTitle=True
print("Title:",)
if name=="author":
self.inAuthor=True
print("Author:",)
def endElement(self,name):
if name=="title":
self.inTitle=False
if name=="author":
self.inAuthor=False
def characters(self,content):
if self.inTitle or self.inAuthor:
print(content)
#end bookHandler
parser=make_parser()
parser.setContentHandler(bookHandler())
parser.parse("library.xml")

解析器xml.sax使用Handler對象解析文檔過程中發生的事件。Handler可能是ContentHandler/DTDHandler /EntityResolver/ErrorHandler一個sax應用程序必須實現符合這些接口的處理程序類,並為解析器設置處理程序

接口ContentHandler包含了被文件事件觸發的方法,例如元素和字符數據的開始和結束等。在解析字符數據時,解析器可以選擇將結果作為一整塊數據返回,或者作為若干小的以空白分隔的數據塊返回,所以在處理一塊文本的過程中需要反復調用characters方法。

make_parser方法創建一個新的解析器對象並將它返回。

 

6 lxml 使用cmd.exe pip install lxml 安裝lxml

lxml是python利用libxml2 和libxslt庫的快速、豐富特性的唯一綁定,並且它通過一個簡單的api允許處理HTML /xml

包lxml使用了略作修改的ElementTreeAPI 

導入lxml:import lxml  

from lxml import etree

元素類:元素是ElementTreeAPI的主要容器對象,提供了xml樹功能的核心,它們擁有屬性並且包含文本.

元素類遵守標准的xml樹層次,因此既能支持父元素也能支持子元素。

>>> import lxml
>>> from lxml import etree

>>> author=etree.Element("Horror") #創建新的元素類author,並賦予一個標簽名稱:Horror
>>> print(author.tag)
Horror

>>> writer1=etree.SubElement(author,"NeilGaiman")# 一個元素的子元素 創建一個新的子元素 ,它的標簽是NeilGaiman,父元素是author
>>> writer2=etree.SubElement(author,"StephenKing")
>>> writer3=etree.SubElement(author,"CliveBarker")
>>> print(etree.tostring(author))
b'<Horror><NeilGaiman/><StephenKing/><CliveBarker/></Horror>'
>>> writer=author[0] #元素類也是列表,可以使用列表函數
>>> print(writer.tag)
NeilGaiman
>>> for writer in author:
print(writer.tag)


NeilGaiman
StephenKing
CliveBarker

元素可以包含屬性,描述元素。

>>> author=etree.Element("author",audience="Adult")
>>> print(author.get("audience")) 
Adult

get()方法可以從元素中提取數據,set()方法設置屬性或都添加屬性

>>> author.set("testpro","protect")

>>> etree.tostring(author)
b'<author audience="Adult" type="fiction" bestseller="Yes" testpro="protect"/>'

還可以向元素中添加文本

>>> html=etree.Element("html")
>>> body=etree.SubElement(html,"body")
>>> h1=etree.SubElement(body,"h1")
>>> h1.text="Introduction"

>>> paragraph=etree.SubElement(body,"p")
>>> paragraph.text="here is some text representing our paragraph"

>>> etree.tostring(html)
b'<html><body><h1>Introduction</h1><p>here is some text representing our paragraph</p></body></html>'

打印元素的文本:

>>> etree.tostring(paragraph,method="text")
b'here is some text representing our paragraph'

 lxml解析函數:

fromstring()

>>> sentence="<info>here is a sentence</info>"
>>> info=etree.fromstring(sentence)
>>> print(info.tag)
info
>>> print(info.text)
here is a sentence

XML()

>>> info=etree.XML("<info>here is a sentence</info>")
>>> print(info.tag)
info

>>> print(info.text)
here is a sentence
>>> etree.tostring(info)
b'<info>here is a sentence</info>'

>>> import io
>>> newsentence=io.StringIO("<info>This is another sentence</info>")
>>> somesentence=etree.parse(newsentence)
>>> etree.tostring(somesentence)
b'<info>This is another sentence</info>'

>>> printit=somesentence.getroot()
>>> print(printit.tag)
info
>>> print(printit.text)
This is another sentence

 

dom解析xml

import xml.dom.minidom
from xml.dom.minidom import parse
dom1=parse(r'E:\pythonscript\ch15\config.xml')
myconfig=dom1.getElementsByTagName("config")[0]
dire=myconfig.getElementsByTagName("utilitydirectory")[0]
print(dire.childNodes[0].data)
uti=myconfig.getElementsByTagName("utility")[0]
print(uti.childNodes[0].data)
mode1=myconfig.getElementsByTagName("mode")[0]
print(mode1.childNodes[0].data)

 

#!/usr/bin/python
from xml.sax import make_parser
from xml.sax.handler import ContentHandler

class configHandler(ContentHandler):
isUtilDir=False
isUtil=False
isMode=False
def startElement(self,name,attributes):
if name=="utilitydirectory":
self.isUtilDir=True
print("------------utility directory-----",)
if name=="utility":
self.isUtil=True
print("--------------utility----------",)
if name=="mode":
self.isMode=True
print("---------------mode--------------",)
def endElement(self,name):
if name=="utilitydirectory":
isUtilDir=False
if name=="utility":
isUtil=False
if name=="mode":
isMode=False

def characters(self,content):
if self.isUtilDir or self.isUtil or self.isMode:
print(content)

parser=make_parser()
parser.setContentHandler(configHandler())
parser.parse(r"E:\pythonscript\ch15\config.xml")

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM