zoukankan      html  css  js  c++  java
  • 用python的minidom解析xml(转载)

    1 xml规则概述
      以下是一个博客网站的xml的例子:

    <?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0">
            <channel>
                    <title>pilgrim</title>
                    <link>http://blog.xxx.com</link>
                    <description>博客描述</description>
                    <generator>Terac Miracle 3.8</generator>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_42678.html</link>
                            <description>文章内容</description>
                            <pubDate>Sun, 23 Sep 2007 23:32:00 +0800</pubDate>
                    </item>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_39749.html</link>
                            <description>文章内容</description>
                            <pubDate>Mon, 27 Aug 2007 23:58:00 +0800</pubDate>
                    </item>
            </channel>
    </rss>

      其中,第一行为文档声明,它向解析器提供了关于文档的基本信息。建议使用 XML 声明,但它不是必需的。如果有的话,那么它一定是文档的第一样东
    西。声明最多可以包含三个名称-值对。version 是使用的 XML 版本;目前该值必须是 1.0。encoding 是该文档所使用的字符集。如没有指定 encoding,XML 解析器会假定字符在 UTF-8 字符集中。最后,还有一个standalone(可以是 yes 或 no)
    定义了是否可以在不读取任何其它文件的情况下处理该文档。例如,如果 XML 文档没有引用任何其它文件,则可以指定standalone="yes"。如果 XML 文档引用其它描述该文档的文件,则可以指定 standalone="no"。
    standalone="no" 是缺省值。
    例:<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
      接下来,定义了xml文档的根元素,一个xml文档必须包含在一个单一的根元素中,在本例中元素标签名为rss,它包含文档中所有文本和所有其它元
    素。version是rss元素的一个属性,其值为字符串"2.0"。属性必须有用引号括起的值,可以是单引号或双引号。</rss>是rss元素结束
    标志,每个元素的结束标记是必须的。如果元素没有子节点,可以结束标记和开始标记合二为一:<tag attr='value'/>。
      xml元素不能重叠,xml区分大小写。

      三种 XML 文档:
        * 无效文档没有遵守 XML 规范定义的语法规则。如果开发人员已经在 DTD 或模式中定义了文档能够包含什么,而某个文档没有遵守那些规
    则,那么这个文档也是无效的。(请参阅定义文档内容以获得对 XML 文档 的 DTD 和模式的专门介绍。)
        * 有效文档既遵守 XML 语法规则也遵守在其 DTD 或模式中定义的规则。
        * 格式良好的文档遵守 XML 语法,但没有 DTD 或模式。

    2 解析器种类
      要让计算机读懂xml文档,就需要解析器(parser)。
      有不同的方法来划分解析器种类:

      1.验证或非验证解析器
      2.支持 Document Object Model (DOM) 的解析器
      3.支持 Simple API for XML (SAX) 的解析器
      4.特定语言编写的解析器 (Java, C++, Perl 等)

      如我们在前面所提及的,XML 文档如果使用一个 DTD 并符合 DTD中的规则将被称为有效文档(valid document)。符合基本标记规则的 XML 文档被称为格式正确文档(well-formed document)。XML 规范要求所有的解析器当其发现一个文档不是格式正确时要报错。
      验证解析器(Validating parser)在解析 XML 文档同时进行验证(检查是否是有效文档)。非验证解析器(Non-validating parser) 忽略所有的验证错误。换而言之,如果一个 XML 文档是格式正确的时,一个非验证解析器并不关注文档是否符合其对应 DTD 所指定的规则(如果有的话)。

      文档对象模型(Document Object Model)是 World Wide Web Consortium(W3C)的正式推荐。它定义了一个接口使得程序可以存取和更新 XML 文档的风格、结构和内容。支持 DOM 的 XML 解析器实现该接口。
      一个 DOM 的解析器在解析一个 XML 文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后你可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入xml文件。

      SAX API 是另一种处理 XML 文档内容的方法。一个既成事实的标准,它由David Megginson 和 XML-Dev 邮件列表
    其它成员所开发。不同于DOM,SAX不是一次性读取整个文档,而是以数据流的形式处理文档,解析器在文档的不同处将产生事件。由您来决定对每个事件如
    何处理。

      DOM解析器适合处理短小的文档,并且适合对文档进行随机访问和修改,SAX适合按顺序读取大型文档。用SAX修改文档是很麻烦的。

    3 DOM与 python 库xml.dom.minidom
      DOM把xml每个元素、属性、文本等信息储存在称为节点的数据类型中,xml.dom中Node(节点)是xml文档中每一个component
    的父类。XML 中最常见的节点类型包括:
    *元素:元素是 XML 的基本构造模块。通常,元素拥有子元素、文本节点,或两者的组合。元素节点也是能够拥有属性的唯一节点类型。
    *属性:属性节点包含关于元素节点的信息,但是并不实际认为是元素的孩子,比如在下面的例子中:
    *文本:文本节点就是名副其实的文本。它可以由更多信息组成,也可以只包含空白。
    *文档:文档节点是文档中其他所有节点的父亲。
    其他节点类型不太常用,但是在某些场合下仍然是必需的。它们包括:
    *CDATA:字符数据(Character Data)的缩写,这是一个特殊的节点,它包含不应该被解析器分析的信息。相反,它包含的信息应该以纯文本传递。例如,可能会为了特殊目的而存储 HTML 标签。在通常情形下,处理器可能尝试为所存储的每个标签创建元素,而这样可能导致文档不是格式良好
    的。这些问题可用通过使用 CDATA 节(section)来避免。这些节使用特殊的符号来编写:
    <[CDATA[<b>
          Important:  Please keep head and hands inside ride at <i>all
    times</i>.
          </b>]]>
    *注释:注释包括关于数据的信息,通常被应用程序忽略。它们写为如下形式:
    <!--  This is a comment. -->
    *处理指令:处理指令是专门针对应用程序的信息。其中一些例子包括要执行的代码或者关于从何处寻找样式表的信息。例如:
    <?xml-stylesheet type="text/xsl" href="foo.xsl"?>
    python xml.dom中节点类型由节点中nodeType属性得到,该属性取如下值:ELEMENT_NODE,
    ATTRIBUTE_NODE, TEXT_NODE, CDATA_SECTION_NODE, ENTITY_NODE,
    PROCESSING_INSTRUCTION_NODE, COMMENT_NODE, DOCUMENT_NODE,
    DOCUMENT_TYPE_NODE, NOTATION_NODE

    示例:把上面的xml例子保存为test-utf8.xml,一定用utf8的编码保存。然后在python提示符下输入:
    >>> from xml.dom import minidom
    >>> xmldoc=minidom.parse('test-utf8.xml')

    得到一个Document类型变量xmldoc, 它是一个保存文档所有信息的树结构。用Node的toxml()函数可以得到节点中保存的xml字符串。因为Document是Node的子类,所以可以应用toxml函数:
    >>> print xmldoc.toxml()

    <?xml version="1.0" ?><rss version="2.0">
            <channel>
                    <title>pilgrim</title>
                    <link>http://blog.xxx.com</link>
                    <description>博客描述</description>
                    <generator>Terac Miracle 3.8</generator>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_42678.html</link>
                            <description>文章内容</description>
                            <pubDate>Sun, 23 Sep 2007 23:32:00 +0800</pubDate>
                    </item>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_39749.html</link>
                            <description>文章内容</description>
                            <pubDate>Mon, 27 Aug 2007 23:58:00 +0800</pubDate>
                    </item>
            </channel>
    </rss>
    要得到文档的根节点,用Document的documentElement属性:
    >>> root=xmldoc.documentElement
    >>> root

    <DOM Element: rss at 0x14f29e0>
    >>> print root.toxml()

    <rss version="2.0">
            <channel>
                    <title>pilgrim</title>
                    <link>http://blog.xxx.com</link>
                    <description>博客描述</description>
                    <generator>Terac Miracle 3.8</generator>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_42678.html</link>
                            <description>文章内容</description>
                            <pubDate>Sun, 23 Sep 2007 23:32:00 +0800</pubDate>
                    </item>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_39749.html</link>
                            <description>文章内容</description>
                            <pubDate>Mon, 27 Aug 2007 23:58:00 +0800</pubDate>
                    </item>
            </channel>
    </rss>
    root有一个子元素channel,这些子元素由root的childNodes保存,其中第一个子节点由root.firstChild引用,最后一个子节点由root.lastChild引用:

    >>> print root.firstChild.toxml()
    >>> print root.lastChild.toxml()
    >>> print root.childNodes[1].toxml()

    <channel>
                    <title>pilgrim</title>
                    <link>http://blog.xxx.com</link>
                    <description>博客描述</description>
                    <generator>Terac Miracle 3.8</generator>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_42678.html</link>
                            <description>文章内容</description>
                            <pubDate>Sun, 23 Sep 2007 23:32:00 +0800</pubDate>
                    </item>
                    <item>
                            <title>文章标题</title>
                            <link>http://blog.xxx.com/e_39749.html</link>
                            <description>文章内容</description>
                            <pubDate>Mon, 27 Aug 2007 23:58:00 +0800</pubDate>
                    </item>
            </channel>
    因为firstChild和lastChild是由channel前后的空白和换行形成的文本型节点,所以会打印出空行。
    访问xmldoc:
    xml各部分与minidom关系对应:
    <Node.tagName Node.attributes.keys() = Node.attributes['key'].value
    attributes可以像dict一样使用,用keys()得到属性列表,用Node.attributes['key'].value得到属性值

            Node.childNodes保存个子节点,第一个节点Node.firstChild,最后一个Node.lastChild,可以像list一样使用。
            <Node.tagName>TextNode.data</Node.tagName>
    </Node.tagName>
    修改xmldoc:
    添加节点:首先创建节点:Document.createElement(tagName)
    Document.createTextNode(data)再把节点添加到Node下面:Node.appendChild()
    node.insertBefore(new,ref)
    删除节点Node.removeChild()
    替换节点 Node.replaceChild(new,old)
    添加删除属性 更改属性
    Element.setAttribute(name,value) Element.removeAttribute(name) 也可以用
    Element.attributes['key']=value来直接指定,value是unicode字符串。

    4 例子
    dir2xml.py是由目录结构生成xml文件的例子,xml2dir.py是根据生成的xml文件内容重建目录,当然建立出的文件都是没有内容的空文件。
    文件dir2xml.py:

    #!/usr/bin/env python
    #-*-   coding:   gbk   -*-
    """遍历目录,根据目录结构生成xml文件。
    dir2xml dirname xmlfilename
    """
    import os
    from xml.dom import minidom as pydom
    import sys
    def usage():
        print "usage:",sys.argv[0],"dirname xmlfilename"

    def dir2xml(dirname):
        """遍历目录,根据目录内容生成xml Document对象
        dirname是路径名,必须是标准的路径名,前后没有空格,后面不带/或\\。
    """
        impl=pydom.getDOMImplementation()
        newdoc=impl.createDocument(None,"dir",None)
        rootdir=newdoc.documentElement
        rootdir.attributes['name']=os.path.basename(dirname)

        def walkdir(dirname,node,document):   #对目录递归遍历,这里用os.path.walk更简单
            for file in os.listdir(dirname):
                if os.path.isfile(os.path.join(dirname,file)):
                    newFileEl=document.createElement('file')
                    newFileEl.attributes['name']=file.encode('utf8')
                    node.appendChild(newFileEl)
                elif os.path.isdir(os.path.join(dirname,file)):
                    newFileEl=document.createElement('dir')
                    newFileEl.attributes['name']=file.encode('utf8')
                    node.appendChild(newFileEl)
                    walkdir(os.path.join(dirname,file),newFileEl,document)
        walkdir(dirname,rootdir,newdoc)
        return newdoc

    if __name__ == '__main__':
        if len(sys.argv)<3:
            usage()
            sys.exit()
        if not os.path.isdir(sys.argv[1]):
            print 'Error:',sys.argv[1],'is not a directory.'
            sys.exit()
        xmlfile=file(sys.argv[2],'w')

    newdoc=dir2xml(unicode(os.path.normpath(sys.argv[1].strip()),'gb2312'))
        newdoc.writexml(xmlfile,'\n','  ')
        xmlfile.close()

    文件xml2dir.py:

    #!/usr/bin/env python
    # -*- coding: cp936 -*-
    """由xml文档生成目录
    xml2dir xmlfilename dirname
    """
    import os
    from xml.dom import minidom as pydom
    import sys

    def usage():
        print "usage:",sys.argv[0],"dirname xmlfilename"

    def xml2dir(xmlElement,dirname):
        """由xml文档生成目录
        xml2dir(xmlElement,dirname)
        xmlElement是xml文档元素,标签名file表示文件,dir表示目录。属性name表示文件或目录名
        dirname是保存xmlElement整个节点的目录名,将在dirname目录中开始建立xmlElement目录树。"""
        if not os.path.exists(dirname):
            os.mkdir(dirname)
        cwd=os.getcwd()
        os.chdir(dirname)
        for childNode in xmlElement.childNodes:
            if childNode.nodeType not in
    (childNode.ELEMENT_NODE,childNode.DOCUMENT_NODE):
                continue
            if childNode.tagName == u'file':
                file(childNode.attributes['name'].value,'w').close()
            elif childNode.tagName == u'dir':
                if not os.path.exists(childNode.attributes['name'].value):
                    os.mkdir(childNode.getAttribute('name'))
                xml2dir(childNode,childNode.getAttribute('name'))
        os.chdir(cwd)

    if __name__ == '__main__':
        if len(sys.argv)<3:
            usage()
            sys.exit()
        try:
            xmlfile=file(sys.argv[1],'r')
        except:
            sys.stderr.write("XML file not found or cannot access.")
            sys.exit()
        xmldoc=pydom.parse(xmlfile)
        xml2dir(xmldoc,os.path.normpath(sys.argv[2].strip()))
        xmlfile.close()

  • 相关阅读:
    [POJ1195] Mobile phones(二维树状数组)
    [SWUST1740] 圆桌问题(最大流)
    [SWUST1759] 骑士共存问题(最大流,最大独立集)
    欧拉函数O(sqrt(n))与欧拉线性筛素数O(n)总结
    BZOJ 1036: [ZJOI2008]树的统计Count-树链剖分(点权)(单点更新、路径节点最值、路径求和)模板,超级认真写了注释啊啊啊
    POJ 3237.Tree -树链剖分(边权)(边值更新、路径边权最值、区间标记)贴个板子备忘
    计蒜客 30999.Sum-筛无平方因数的数 (ACM-ICPC 2018 南京赛区网络预赛 J)
    洛谷 P3383 【模板】线性筛素数-线性筛素数(欧拉筛素数)O(n)基础题贴个板子备忘
    计蒜客 30996.Lpl and Energy-saving Lamps-线段树(区间满足条件最靠左的值) (ACM-ICPC 2018 南京赛区网络预赛 G)
    计蒜客 30990.An Olympian Math Problem-数学公式题 (ACM-ICPC 2018 南京赛区网络预赛 A)
  • 原文地址:https://www.cnblogs.com/licheng/p/1897657.html
Copyright © 2011-2022 走看看