python解析、构建 xml

Overview

这篇博客内容将包括对 XML 文件的解析、追加新元素后写入到 XML,以及更新原 XML 文件中某结点的值。使用的是 python 的xml.dom.minidom包,详情可见其官方文档:xml.dom.minidom 官方文档

解析 XML 文件、字符串

导包

parse()和parseString()函数所做的是将一个XML解析器与一个DOM构建器连接起来,这个DOM构建器可以接受来自任何SAX解析器的解析事件,并将它们转换成一个DOM树。

from xml.dom.minidom import parse, parseString

解析

parseparseString 方法都会返回一个表示文档的内容的Document对象。

解析文件

传入文件名,或者类似于文件的对象都可。

# 方式一:
dom1 = parse('c:\\temp\\mydata.xml')

# 方式二(推荐): 可自动释放资源
with parse('c:\\temp\\mydata.xml') as dom1:
    pass

# 方式三:
datasource = open('c:\\temp\\mydata.xml')
dom2 = parse(datasource)  # parse an open file

解析字符串

CDATA:在 XML 中,不会被解析器解析的部分数据。

message = '''
<!-- This is list of customers -->
<customers>
  <customer ID="C001">
    <name>Acme Inc.</name>
    <phone>12345</phone>
    <comments>
      <![CDATA[Regular customer since 1995]]>
    </comments>
  </customer>
  <customer ID="C002">
    <name>Star Wars Inc.</name>
    <phone>23456</phone>
    <comments>
      <![CDATA[A small but healthy company.]]>
    </comments>
  </customer>
</customers>
'''
# 方式一:
dom3 = parseString(message)

# 方式二:
# 将字符串转为 `io.String`对象
import io

msg = io.StringIO(message)
dom4 = parse(msg)
print(type(dom4))
<class 'xml.dom.minidom.Document'>

常用方法、属性

一旦有了DOM文档对象,就可以通过其属性和方法访问XML文档的各个部分。这些属性在DOM规范中定义。文档对象的主要属性是documentElement属性。它提供了XML文档中的根元素

在解析 XML 时,所有的文本都是储存在文本节点中的,且该文本节点被视为元素结点的子结点,例如:2005,元素节点 ,拥有一个值为 “2005” 的文本节点,“2005” 不是 元素的值,最常用的方法就是 getElementsByTagName() 方法了,获取到结点后再进一步根据文档结构解析即可。

具体的理论就不过多描述,配合上述 XML 文件和下面的代码,你将清楚的看到操作方法,下面的代码执行的工作是将所有的结点名称以及结点信息输出一下:

# 文档根元素
rootNode = dom4.documentElement
rootNode
<DOM Element: customers at 0x2cc4551d930>
# 获取所有子节点
rootNode.childNodes
[<DOM Text node "'\n  '">,
 <DOM Element: customer at 0x2cc456ab800>,
 <DOM Text node "'\n  '">,
 <DOM Element: customer at 0x2cc456aba60>,
 <DOM Text node "'\n'">]
# 获取第一个子节点
first_node = rootNode.firstChild
# 判断节点类型
first_node.nodeType == first_node.TEXT_NODE
True
# 兄弟节点
# 第一个节点的兄弟节点即父节点的第二个子节点
first_node.nextSibling == rootNode.childNodes[1]
True
# 根据节点名称查找,返回列表
rootNode.getElementsByTagName('customer')
[<DOM Element: customer at 0x2cc45f2aa60>,
 <DOM Element: customer at 0x2cc45f2acc0>]
# 可以查找任意层级的节点
rootNode.getElementsByTagName('phone')
[<DOM Element: phone at 0x2cc45f2ab90>, <DOM Element: phone at 0x2cc45f2adf0>]
# 通过索引获取一个节点
rootNode.getElementsByTagName('customer')[1]

rootNode.childNodes[1]
<DOM Element: customer at 0x2cc45f2aa60>
customer = rootNode.getElementsByTagName('customer')[0]

# 节点名
print('tagName:', customer.tagName)
print('nodeName:', customer.nodeName)

# 节点属性
if customer.hasAttribute('ID'):
    print("ID:", customer.getAttribute("ID"))
    
# 获取节点文本数据, 先获取一个叶子节点
name_node = customer.getElementsByTagName('name')[0]
# 再获取其文本节点
text_node = name_node.childNodes[0]
# 或者
# text_node = name_node.firstChild

print('Text data : ', text_node.data)
print('Text value: ', text_node.nodeValue)
print('Text xml  :', text_node.toxml())
tagName: customer
nodeName: customer
ID: C001
Text data :  Acme Inc.
Text value:  Acme Inc.
Text xml  : Acme Inc.
# 转字符串
print(customer.toxml(encoding=None))
<customer ID="C001">
    <name>Acme Inc.</name>
    <phone>12345</phone>
    <comments>
      <![CDATA[Regular customer since 1995]]>
    </comments>
  </customer>
# 显示缩进
print(customer.toprettyxml(indent='  ', newl=''))
<customer ID="C001">  
      <name>Acme Inc.</name>  
      <phone>12345</phone>  
      <comments>    
      <![CDATA[Regular customer since 1995]]>    
      </comments>  
  </customer>
# 写入文件
with open('added_customer.xml', 'w') as f:
    # 缩进 - 换行 - 编码
    dom4.writexml(f, indent='  ', addindent='', newl='', encoding='utf-8')

写入 XML 文件

在写入时,我觉得可分为两种方式:

  • 新建一个全新的 XML 文件
  • 在已有 XML 文件基础上追加一些元素信息

至于以上两种情况,其实创建元素结点的方法类似,你必须要做的都是先创建 / 得到一个 DOM 对象,再在 DOM 基础上创建 new 一个新的结点。

如果是第一种情况,你可以通过dom=minidom.Document()来创建;如果是第二种情况,直接可以通过解析已有 XML 文件来得到 dom 对象,例如dom = parse("./customer.xml")

在具体创建元素 / 文本结点时,你大致会写出像以下这样的 “四部曲” 代码:

  • ①创建一个新元素结点 createElement()
  • ②创建一个文本节点 createTextNode()
  • ③将文本节点挂载元素结点上
  • ④将元素结点挂载到其父元素上。

现在,我需要新建一个 customer 节点,信息如下:

<customer>
    <name ID='003'>蒂法</name>
    <phone>32467</phone>
    <comments>
      <![CDATA[A small but healthy company.]]>
    </comments>
</customer>
from xml.dom.minidom import getDOMImplementation

impl = getDOMImplementation()

newdoc = impl.createDocument(None, "customer", None)
top_element = newdoc.documentElement

split_text_node = newdoc.createTextNode('\n  ')

name_node = newdoc.createElement('name')
name_node.setAttribute('ID', '003')
name_text = newdoc.createTextNode('蒂法')
name_node.appendChild(name_text)

phone_node = newdoc.createElement('phone')
phone_text = newdoc.createTextNode('32467')
phone_node.appendChild(phone_text)

comments = newdoc.createElement('comments')
comments_text = newdoc.createCDATASection('aaaa')
comments.appendChild(comments_text)

top_element.appendChild(name_node)
top_element.appendChild(phone_node)
top_element.appendChild(comments_node)
print(top_element.toprettyxml(indent='    '))
<customer>
    <name ID="003">蒂法</name>
    <phone>32467</phone>
    <comments>
<![CDATA[A small but healthy company.]]>    </comments>
</customer>

第二种方法代码如下:

# 已有文本
message = '''
<customers>
  <customer ID="C001">
    <name>Acme Inc.</name>
    <phone>12345</phone>
    <comments>
      <![CDATA[Regular customer since 1995]]>
    </comments>
  </customer>
</customers>
'''

domTree = parseString(message)
# 文档根元素
rootNode = domTree.documentElement

# 新建一个customer节点
customer_node = domTree.createElement("customer")
customer_node.setAttribute("ID", "C003")

# 创建name节点,并设置textValue
name_node = domTree.createElement("name")
name_text_value = domTree.createTextNode("蒂法")
name_node.appendChild(name_text_value)  # 把文本节点挂到name_node节点
customer_node.appendChild(name_node)

# 创建phone节点,并设置textValue
phone_node = domTree.createElement("phone")
phone_text_value = domTree.createTextNode("32467")
phone_node.appendChild(phone_text_value)  # 把文本节点挂到name_node节点
customer_node.appendChild(phone_node)

# 创建comments节点,这里是CDATA
comments_node = domTree.createElement("comments")
cdata_text_value = domTree.createCDATASection(
    "A small but healthy company.")
comments_node.appendChild(cdata_text_value)
customer_node.appendChild(comments_node)

rootNode.appendChild(customer_node)
print(rootNode.toprettyxml(indent='    ',newl='\n'))
<customers>
    
  
    <customer ID="C001">
        
    
        <name>Acme Inc.</name>
        
    
        <phone>12345</phone>
        
    
        <comments>
            
      
<![CDATA[Regular customer since 1995]]>            
    
        </comments>
        
  
    </customer>
    

    <customer ID="C003">
        <name>蒂法</name>
        <phone>32467</phone>
        <comments>
<![CDATA[A small but healthy company.]]>        </comments>
    </customer>
</customers>

更新 XML 文件

在更新 XML 时,只需先找到对应的元素结点,然后将其下的文本结点或属性取值更新即可,然后保存到文件,具体我就不多说了,代码中我将思路都注释清楚了,如下:

domTree = parse("./customer.xml")
# 文档根元素
rootNode = domTree.documentElement

names = rootNode.getElementsByTagName("name")
for name in names:
    if name.childNodes[0].data == "Acme Inc.":
        # 获取到name节点的父节点
        pn = name.parentNode
        # 父节点的phone节点,其实也就是name的兄弟节点
        # 可能有sibNode方法,我没试过,大家可以google一下
        phone = pn.getElementsByTagName("phone")[0]
        # 更新phone的取值
        phone.childNodes[0].data = 99999

with open('updated_customer.xml', 'w') as f:
    # 缩进 - 换行 - 编码
    domTree.writexml(f, addindent='  ', encoding='utf-8')

参考:

  • https://docs.python.org/3.8/library/xml.dom.minidom.html
  • https://blog.csdn.net/qq_37174526/article/details/89489212
  • https://docs.python.org/3.8/library/xml.html#xml-vulnerabilities
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值