专栏首页python3Python XML解析之DOM

Python XML解析之DOM

DOM说明:

DOM:Document Object Model API

DOM是一种跨语言的XML解析机制,DOM把整个XML文件或字符串在内存中解析为树型结构方便访问。

https://docs.python.org/2/library/xml.dom.html

https://docs.python.org/2/library/xml.dom.minidom.html

xml.dom.minidom就是DOM在Python中实现,本文主要结合minidom解释DOM架构。

API导入:

from xml.dom.minidom import parse
from xml.dom.minidom import parseString
import xml.dom.minidom
dom和etree是xml package目录下的两个subpackage,minidom和ElementTree是dom和etree下的两个module文件,以.py后缀,其中定义了一系列的类和方法。
Document.documentElement相当于Etree中的tree.getroot()用于获取整个树唯一的根节点

概念解析:

xml.dom中包含以下类:

1.DOMImplementation
2.Node 
Node是最重要的类,XML被解析为一个树,所有的节点都是都是node的子类,这些节点可以是element、comments等等,官网列出的节点类型就有:
ELEMENT_NODE, ATTRIBUTE_NODE, TEXT_NODE, CDATA_SECTION_NODE, ENTITY_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE,DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE,每个节点有一个数字表示,只要是node类型就可以使用node.nodeType来判断他属于哪一种node,例如if child.nodeType==child.ELEMENT_NODE:或者if child.nodeType==1:  --两者等价
3.NodeList --通过getElementsByTagName()方法返回的nodelist,此方法只有element和document两个类有。
4.DocumentType
5.Document --整个XML文件解析树,包含所有element、attribute、comments、text等等,也是node的子类。
6.Element 
7.Attr --element的属性,这个类型的node只能由Element.getAttributeNode(attrname)来获取,无法遍历获取,而且其既不是element node的子节点也不是兄弟节点。几乎从无必要获取此节点,直接使用element类的getAttribute(attrname)来得到属性的值即可。
8.Comment --comment节点,表示XML文件注释节点
9.Text  --xml.etree.ElementTree中的text表示的是element中的内容,而这里的text类型表示一个node,这个node可以是element中的data节点也可以是element之间的换行和制表符(\n\t),如果是element的data内容那么此text是element的唯一子节点,通过childNodes[0].data或firstChild.data获取element内容,如果是换行制表符那么此节点element的兄弟节点。
10.ProcessingInstruction
除node类之外,对于XML解析最重要的就是Document类和Element、text、Comment、Attr等类,前者配合parse()或parseString()将xml文件或字符串在内存中实例化为一个tree(document类型),后边的类用于对XML树做各种操作和查询。

鉴于几乎所有的可操作对象类都是继承于node类,这里贴一下node的各种属性和方法的链接:

https://docs.python.org/2/library/xml.dom.html#node-objects

另外再列出node一些常见的属性和方法:
Node.nodeType  --详见上边对Node类的解释
Node.attributes  --只有element类型的node才有此属性
Node.childNodes 
--返回节点的子节点nodelist,与通过getElementsByTagName()获取nodelist的区别在于此方法只返回直接子节点而非全部子节点,此外这两个方法的最大区别是:childNodes返回的是所有子节点的集合,而getElementsByTagName(tagName)必须指定tagName。
Node.previousSibling --node的左兄弟节点,如果没有则返回none
Node.nextSibling --node的右兄弟节点,如果没有则返回none
Node.nodeName --不常用,因为继承于node的各种类都有自己的更便于识别的name属性,例如element.tagName
Node.appendChild(newChild)

另:如果要熟练的使用minidom API,那么请务必将https://docs.python.org/2/library/xml.dom.html 熟读,以上列出的各种继承于node的类都有一些自己独特的属性和方法,除了熟悉node类之外,熟悉这些继承子类的方法也是很有必要的。

XML文件解析示例:

--有一个如下的XML文件:proxool.xml:
<?xml version="1.0" encoding="utf-8"?>
<something-else-entirely>
    <proxool>
        <alias>myPool</alias>
            <!-- mysql 连接配置,注意修改database_hostname为相应的数据库主机名、或IP地址 --> 
         <driver-url>
            jdbc:mysql://dbsrv:3306/TEST?useUnicode=true&characterEncoding=UTF8
        </driver-url> 
        <driver-class>com.mysql.jdbc.Driver</driver-class> 
        <!-- 用户名、密码 -->
        <driver-properties>
            <property name="user" value="leo" />
            <property name="password" value="leo" />
        </driver-properties>
        <!--自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁 -->
        <house-keeping-sleep-time>30000</house-keeping-sleep-time>
        <house-keeping-test-sql>select CURRENT_DATE from dual
        </house-keeping-test-sql>
        <!--最大连接数(默认5个),超过了这个连接数,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定 -->
        <maximum-connection-count>120</maximum-connection-count>
        <!--最小连接数(默认2个) -->
        <minimum-connection-count>5</minimum-connection-count>
        <!--没有空闲连接可以分配而在队列中等候的最大请求数,超过这个请求数的用户连接就不会被接受,该参数已经不建议使用,由simultaneous-build-throttle替代 -->
        <!--一个活动连接最大活动时间默认5分钟 -->
        <maximum-active-time>3600000</maximum-active-time>
        <!--最少保持的空闲连接数(默认2个),如果当前的连接池中的可用连接少于这个数值, 新的连接将被建立 -->
        <prototype-count>5</prototype-count>
        <!--可一次建立的最大连接数 -->
        <simultaneous-build-throttle>20</simultaneous-build-throttle>
        <!--如果为true,那么每个被执行的SQL语句将会在执行期被log记录 -->
        <trace>false</trace>
    </proxool>
</something-else-entirely>

现在将其中的内容解析为如下格式:

*****
描述:最大连接数(默认5个),超过了这个连接数,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定
配置项:maximum-connection-count
配置值:120
*****
描述:xxx
配置项:xxx
配置值:xxx
*****
......

代码如下:

# -*- coding:utf-8 -*-
# 本脚本适用于Python2和3
from xml.dom.minidom import parse
import xml.dom.minidom
import sys
# file = sys.argv[1]
file = "/root/proxool.xml"
# 先写一个判断节点是否包含element类型子节点的判断函数
def has_element_child(nodename):
    has_element_child = 0
    for child in nodename.childNodes:
        if child.nodeType==1:
            has_element_child += 1
    return has_element_child
# 定义解析示例XML文件的方法
def parse_xml(file):
    if not file:
        sys.exit(0)
    tree = parse(file) # document类型的解析树
    root = tree.getElementsByTagName('proxool')[0] # 将父节点定位到proxool element
    for child in root.childNodes:
        if child.nodeType==child.ELEMENT_NODE and has_element_child(child)==0: # 当node为element类型,且无element类型的子节点时
            print u'配置项'+": %s" % child.tagName
            print u'配置值'+": %s" % child.firstChild.data.strip()
        elif child.nodeType==child.ELEMENT_NODE and has_element_child(child)>0: # 当节点包含element类型子节点时
            for child_child in child.childNodes:
                if child_child.nodeType==child.ELEMENT_NODE:
                    print u'配置项'+": %s" % child_child.getAttribute('name')
                    print u'配置值'+": %s" % child_child.getAttribute('value')
        elif child.nodeType==child.COMMENT_NODE: # 当node为comment类型时
            print "*****"
            print u'描述'+": %s" % child.data
        else:
            pass
# 处理示例XML文件
parse_xml(file)

XML文件比较修改示例:

minidom相比于DOM API最大的差别就是添加了node.writexml()、node.toprettyxml()等方法,这两个方法可以将你对XML解析树作出的修改写入文件中,现在我们将proxool.xml copy到proxool.xml.new中,并在proxool节点下添加一个子节点<new_tag name="Leo">For_Test</new_tag>,我们要比较新XML文件中比旧XML文件新增的配置项,对旧XML的配置项不做修改,代码如下:

# -*- coding:utf-8 -*-
# 本脚本适用于Python2和3
from xml.dom.minidom import parse
import xml.dom.minidom
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
old_file = sys.argv[1]
new_file = sys.argv[2]
# 先写一个判断节点是否包含element类型子节点的判断函数
def has_element_child(nodename):
    has_element_child = 0
    for child in nodename.childNodes:
        if child.nodeType==1:
            has_element_child += 1
    return has_element_child
# 定义解析示例XML文件的方法
def match_xml(old_file,new_file):
    if not new_file:
        sys.exit(0)
    tree_old = parse(old_file) # document类型的解析树
    tree_new = parse(new_file)
    root_old = tree_old.getElementsByTagName('proxool')[0] # 将父节点定位到proxool
    root_new = tree_new.getElementsByTagName('proxool')[0]
    old_dict = {} # 定义旧XML文件的tag和data的字典
    new_dict = {}
    for child in root_old.childNodes: #将tagName和data存入old_dict{}中
        if child.nodeType==child.ELEMENT_NODE and has_element_child(child)==0: # 当node为element类型,且无element类型的子节点时
            old_dict[child.tagName] = child.firstChild.data.replace("\n", "").replace("\t", "")
    for child in root_new.childNodes:
        if child.nodeType==child.ELEMENT_NODE and has_element_child(child)==0:
            new_dict[child.tagName] = child.firstChild.data.replace("\n", "").replace("\t", "")
    for tag,data in new_dict.items():
        if not old_dict.get(tag):  # 当旧XML中找不到对应的tag时,进行tag新增操作
            new_element=tree_new.getElementsByTagName(tag)
            for child in new_element:
                root_old.appendChild(child) # 新增element节点
    with open('proxool_modified.xml','w') as f:
        tree_old.writexml(f)
        f.close
# 处理示例XML文件
match_xml(old_file,new_file)
--比较XML文件:
# python xml_match_dom.py proxool.xml proxool.xml.new
--然后就可以在proxool_modified.xml中看到新的XML内容了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python中TCP协议的理解

    py3study
  • IO多路复用丶基于IO多路复用+sock

    py3study
  • 网络编程知识-socket

      网卡--> mac地址-->ip地址-->子网掩码-->网关-->DNS服务器(进行域名domain name 和与之相对应的ip地址转换的服务器)

    py3study
  • 卷积神经网络概述

    在 2012 年的 ILSVRC 比赛中 Hinton 的学生 Alex Krizhevsky 使用深度卷积神经网络模型 AlexNet 以显著的优势赢得了比...

    磐创AI
  • 设置默认的超时和重试是一个基础设施的基本素养

    本篇应该是稳定性「三十六计」系列的一篇:超时重试。但是「设置默认的超时和重试是一个基础设施的基本素养」这句话我在我们组内三次开会的时候都说了。表达了我的一个理念...

    静儿
  • gulp命令执行报错解决

    薛定喵君
  • 用 Hinton 的胶囊神经网络来识别空间关系 Part1: CNNs及其缺点

    “普通的”卷积神经网络是当前机器学习领域最热的一个概念, 但是它也有一些问题,例如不能识别空间关系,比如说图片或其他数据里不同部分之间的相对位置关系。Hinto...

    AI研习社
  • 解放你的双手,陪爸妈看春晚去!

    张宏伦
  • 自动化工程师——安全先行!

    我是一名自动化工程师,我很骄傲的说我是搞工业机器人的,搞设备控制的,智能化车间离不开我们。

    李英杰同学
  • 微信小程序助力实体婚纱店解决引流问题

    实体行业如今的发展有已经离不开互联网,所以如何更好的与互联网对接就成为了一个重点问题。如今实体婚纱店的竞争强度越来越大,因此,婚纱实体店开始通过微信小程序来应对...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券