python之lxml处理xml
? ? 學習過程中遇到了通信的報文為xml的的消息體,將通訊的內容依附于xml的載體進行傳輸,開始嘗試使用包括ElementTree等在內的諸多庫,但是因為一些處理皆不盡人意,最后選擇了lxml庫,該庫無論處理速度還是函數功能封裝基本可以滿足需求。因為lxml不是Python自帶的標準庫,因此需要自己安裝
pip3 install lxml對于讀取xml常見的有兩種方式,一種是xml的字符串,即字符串的內容是xml文件,另一種是工程中包含xml文件,讀取xml后進行操作,由于實際運用中一般不會直接讀取一段字符串的形式(自己遇到的),都是以xml文件的形式進行讀取,因此這里也以也是先讀取文件,再操作文件。這里以? intuit.xml? 文件為例
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Intuit xmlns="http://schema.intuit.com/finance/v3" time="2016-10-14T10:48:39.109-07:00"><QueryRequest startPosition="1" maxResults="79" totalCount="79"><Bill domain="QBO" sparse="false"><Id>=1</Id><name>zhagsan</name><MsgId>20200406</MsgId></Bill></QueryRequest><QueryResponse startPosition="1" maxResults="79" totalCount="79"><Bill domain="QBO" sparse="false"><Id>=2</Id><name>lisi</name><responseId>20200407</responseId></Bill></QueryResponse> </Intuit>在xml文件的開頭明顯的發現不同于常規的xml文件,因為攜帶了命名空間xmlns,因此與沒有命名空間的xml相比每一個節點前都用命名空間,如下:
# 讀取xml文件 xml = etree.parse("Intuit.xml") root = xml.getroot() #獲取根節點 # 獲取命名空間 ns = {'x':root.nsmap[None]} print(ns) {'x': 'http://schema.intuit.com/finance/v3'}xml中每一個節點通常有3個特性,分別是標簽tag,屬性attrib,文本text,因此為了獲取某一個節點的以上特性,需要查找獲取到需要的結點,常用的方法可以是遍歷查找,一種是遍歷所有,另一種是遍歷某個節點
print("遍歷所有節點:") for node in root.iter():print(node.tag)print("遍歷指定節點:") ns = "{http://schema.intuit.com/finance/v3}" for node in root.iter(f"{ns}name"):print(node.tag)輸出結果為:
當然還有一些其他的查找方法如find,findall,具體使用方法在這里不再贅述,參考xml文檔
常見的還有獲取屬性的函數如:
#獲取屬性 print(root.items()) #獲取全部屬性和屬性值 print(root.keys()) #獲取全部屬性 print(root.get('version', '')) #獲取具體某個屬性此外使用xpath操作xml是非常常用且重要的功能的,自己在學習中希望的是知道某個節點tag,去修改text值,然后發送修改后的xml文件的消息到服務器,對于xpath,其他的文章已有介紹。對于沒有命名空間的可以通過相對路徑或者絕對路徑進行訪問:
#通過相對路徑 root.xpath('//name')#通過絕對路徑 root.xpath('Intuit/QueryResponse/name')但這里自然是訪問不了的,因為有命名空間的限制,因此有命名空間的話:
# 導入庫 import lxml.etree as etree# 讀取xml文件 xml = etree.parse("Intuit.xml") root = xml.getroot() #獲取根節點# 獲取命名空間 ns = {'x':root.nsmap[None]} node = root.xpath("//x:name",namespaces=ns) print(node)輸出結果:
[<Element {http://schema.intuit.com/finance/v3}name at 0x263aa0d4d88>, <Element {http://schema.intuit.com/finance/v3}name at 0x263aa0d4e88>]注意這里輸出的結果是 list,因此比如需要修改的是? QueryResponse/Bill/name這個標簽的性質,可以使用
node[1].text = "4"當然還可以通過絕對路徑進行修改
node = root.xpath("//x:Intuit//x:QueryResponse//x:Bill//x:name",namespaces=ns)其實我們也發現使用絕對路徑貌似有點sha哈,一個是路徑深度太深,第二個是每一個結點都需要加上//x:*使用起來非常麻煩,因此如果能唯一定位,比如MsgId或者是responseId這樣最方便,但是有些結點重復且深度較深。對于結點的定位使用xpath有以下三種形式:
xml = etree.parse("Intuit.xml") root = xml.getroot() #獲取根節點# 獲取命名空間 ns = {'x':root.nsmap[None]}MsgId = root.xpath("//x:MsgId",namespaces=ns) print(MsgId[0].text)name = root.xpath("//x:QueryResponse//..//x:name",namespaces=ns) print(name[0].text)Id = root.xpath("//x:Intuit//x:QueryResponse//x:Bill//x:Id",namespaces=ns) print(Id[0].text)注意:如果是使用相對路徑中含有..的形式,該..前不能加上//x:*。
當然實際使用過程中發現xml結點較多,且使用多個不同的xml文件,因為每一個xml都有命名空間,因此封裝了文件用于操作xml。
# 導入庫 import reimport lxml.etree as etree# 讀取xml文件 import osclass XmlOperation():def __init__(self):passdef readXml(self,fileName):path = os.path.join("..","resource",fileName)tree = etree.parse(path)self.root = tree.getroot()return self#region設置文本節點def setNodeText(self,xpath="",replaceName="",index=0):ns = {'x':self.root.nsmap[None]}path = ""#如果傳入的xpath以/開頭或者/結尾,則去除開頭或者結尾的/if re.match(r"^(/).+?",xpath) or re.match(r".+?(/)$",xpath):path = xpath.strip("/")#可以唯一定位,如MsgIdif not re.search(r"/",xpath):path = "//x:" + xpathelif re.search(r"/",xpath):pathList = xpath.split("/")#路徑拼接path = "//x:" + "//x:".join(pathList)pattern = re.compile(r"(x:\W{2)")#如果是//x:A//x:..//x:c的形式修改修改為//x:A//..//x:cif pattern.search(path):path = path.replace(r"x:..",r"..")else:raise Exception("路徑輸入有誤")self.root.xpath(path,namespaces=ns)[index].text = replaceName#endregion#region刪除屬性def del_node_attrib(self,node,attrib):if node.getchildren():for child in node.getchildren():del_node_attrib(child,attrib):else:attri = node.attrib.get(attrib)if attri and attri == "√":del node.attrib[attrib]#endregion這里再插入一點小常識:
1.使用pycharm的時候,生成類(大寫首字母,寫完類名)按下Alt+Enter,導入庫(比如直接使用os.path選中os)按下Alt+Enter都是很好的快捷方法
2.函數的編寫,加入#region...#endregion,可以更方便的瀏覽函數
寫在最后,在學習使用lxml處理的時候遇到了不少問題,推薦幾篇入門的帖子,對于如何使用lxml中攜帶有命名空間的比較有幫助,python讀取xml,和命名空間使用以及lxml處理命名空間
總結
以上是生活随笔為你收集整理的python之lxml处理xml的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python-Requests.post
- 下一篇: Python之collections容器