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容器