xml
*之前用的時候也沒想到。。其實用BeautifulSoup就可以解析xml啊。。因為html只是xml的一種實現方式吧。但是很蛋疼的一點就是,bs不提供獲取對象的方法,其find大多獲取的都是字符串,這就導致不得不一遍遍地連續通過bs總對象來定位元素再輸出這樣子。挺麻煩的。
xml是一種常用的網絡通訊格式,也是一種文件的格式。xml包里有多種不同的可用於解析&生成文件的模塊,比如:
xml.dom.minidom
xml.etree.ElementTree
xml.aix等等。這三者比較起來,minidom最貼近底層代碼,但是寫起來也最煩。本文主要通過ElementTree來解析&生成。盡管ElementTree也有很多不合理的地方,但是可以通過修改源碼,重載源碼的部分方法來解決。
■ ElementTree基本用法
from xml.etree import ElementTree doc = ElementTree.parse("文件路徑"or"文件對象"[,parser=...]) ''' 雖然也可用ElementTree直接來生成文件對象,但是用parse來生成的話可以指定parser。這個parser后面會講到有什么作用 '''
所得到的doc對象就是文件的根節點對象。所謂根節點,就是整個文件第一對有效的標簽所指代的節點。
● 對於每個節點,都有:
.text 代表該節點的文本內容(和bs一樣,那些有子孫元素但是本身沒有內容的節點的這個屬性是空串,需要注意)
.attrib 代表該節點的屬性(字典形式)
.tag 代表該節點的tag名
● 而對於包括根節點在內的所有節點都有以下節點可調用的方法:
.find("tag名") 找出其子節點中第一個指定tag名的節點
.findall("tag名") 找出所有子節點which是指定的tag名,返回一個列表
.findtext("tag名") 找出相應子節點的文本內容,相當於上面的find找到對象之后再訪問其.text屬性
//以上三個方法都是只檢索子節點,不檢索孫節點以及更加后輩的節點
.getiterator([tag]) 從當前節點開始,生成一個迭代器,里面是遍歷了包括當前節點在內的所有后輩節點。tag參數相當於一個filter,指定tag的話迭代器只返回tag名和給出值一致的節點。
*以上的那些find啥啥的方法,里面的tag名其實是有語法的。單純的"tag"就只檢索當前節點的子節點中名為tag的節點。如果是'.//tag'的話就是搜索所有后輩節點中的tag節點了。更多具體的語法可以參見python參考手冊,就不多說了
● 另外,ElementTree也在節點對象中實現了幾個magic method,所以節點(這次不包括根節點了)也有了如下借口:
elem[n] 節點elem的第n個子節點(不是n+1個!)
del elem[n] 刪除elem的第n個子節點
len(elem) 子節點的個數
elem[n] = newElement 將某個子節點替換成另外一個element
● 對於非根節點,還有以下的一些方法:
clear() 清空所有后輩節點
append(Element) 加入一個新子節點
get(key) 獲取某個屬性的值
insert(index,Element) 將子節點插入某個特定的位置
remove(Element) 從該節點中移除一個子節點
set(key,value) 設置某個節點的屬性值
● 關於Element的構造方法
上面很多方法都用element作為參數,那么element是怎么來的,就要用到Element構造方法了。
ElementTree.Element(tag[,attrib]) 構造一個Element,但是沒有文本內容
ElementTree.XML("xmlcode") 將一段xml代碼轉化為一個Element對象,比較實用
ElementTree.Comment("text") 生成一段注釋的Element對象
● 最后,ElementTree還提供了幾個類方法
ElementTree.dump(Element) 把相關element的內容打印出來,主要用於調試。因為element對象普遍沒有實現__repr__方法
ElementTree.iselement(element) 判斷某個對象是不是有效的element對象
以上的所有操作都是對存儲在內存中的一個XMLTree對象的改動,要想保存成文件,只要根節點調用方法write即可
doc.write("文件路徑"or"w模式文件對象"[,encoding=xxx])
這里面的encoding參數也有點意思。encoding的默認值是utf-8,當encoding被指定且不是utf-8或者ascii的時候,在新生成的文件頭上自動會加一條<?xml version="1.0" encoding="xxx">
*ElementTree有一個很大的問題,就是在默認情況下,會吃掉所有注釋內容。
這種現象的原因是因為,源碼中默認的TreeBuilder(這個類的作用是構建一個對象,這個對象用來存儲抽象化后的文件內容。)在建立Tree對象的時候沒有寫處理注釋的方法。
解決方法是自己寫個CommentedTreeBuilder類來重載處理注釋:
class CommentedTreeBuilder(XMLTreeBuilder): def __init__(self, html=0, target=None): XMLTreeBuilder.__init__(self, html, target) self._parser.CommentHandler = self.handle_comment #指定處理comment的方法。 def handle_comment(self, data): ''' 默認的處理comment的方法是什么都不做直接pass,而在這個方法里,通過start,data和end三個方法,相當於把注釋的內容原封不動地復制到創建的Tree對象里去,使得注釋得以保存 ''' self._target.start(Comment, {}) self._target.data(data) self._target.end(Comment)
handle_comment方法接受的參數data是一個ascii字符串或者unicode字符串。當有中文字符時無疑data是unicode。如果在write的時候不指明encoding類型的話可能會出現寫入中文字符出錯,變成其編碼的格式了。解決辦法就是在write的時候指出encoding參數如encoding='UTF-8'