文檔模型:用以描述詞匯和文檔結構,定義文檔中將要出現的數據元素,元素之間的關系,以及元素的數量等
實現文檔模型的方法:模式 和 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")