XML 树结构,语法规则,元素,属性,验证及其解析

XML 文档形成了一种树结构,它从"根部"开始,然后扩展到"枝叶"。


一个 XML 文档实例

XML 文档使用简单的具有自我描述性的语法:

<?xml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>

第一行是 XML 声明。它定义 XML 的版本(1.0)和所使用的编码(UTF-8 : 万国码, 可显示各种语言)。

下一行描述文档的根元素(像在说:"本文档是一个便签"):

<note>

接下来 4 行描述根的 4 个子元素(to, from, heading 以及 body):

<to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body>

最后一行定义根元素的结尾:

</note>

您可以假设,从这个实例中,XML 文档包含了一张 Jani 写给 Tove 的便签。

XML 具有出色的自我描述性,您同意吗?


XML 文档形成一种树结构

XML 文档必须包含根元素。该元素是所有其他元素的父元素。

XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。

所有的元素都可以有子元素:

<root> <child> <subchild>.....</subchild> </child> </root>

父、子以及同胞等术语用于描述元素之间的关系。父元素拥有子元素。相同层级上的子元素成为同胞(兄弟或姐妹)。

所有的元素都可以有文本内容和属性(类似 HTML 中)。


实例:

DOM node tree

上图表示下面的 XML 中的一本书:

XML 文档实例

<bookstore> <book category="COOKING"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="CHILDREN"> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="WEB"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore>

实例中的根元素是 <bookstore>。文档中的所有 <book> 元素都被包含在 <bookstore> 中。

<book> 元素有 4 个子元素:<title>、<author>、<year>、<price>。

XML 语法规则


XML 的语法规则很简单,且很有逻辑。这些规则很容易学习,也很容易使用。


XML 文档必须有根元素

XML 必须包含根元素,它是所有其他元素的父元素,比如以下实例中 root 就是根元素:

<root> <child> <subchild>.....</subchild> </child> </root>

以下实例中 note 是根元素:

<?xml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>

XML 声明

XML 声明文件的可选部分,如果存在需要放在文档的第一行,如下所示:

<?xml version="1.0" encoding="utf-8"?>

以上实例包含 XML 版本(<version="1.0"),甚至包含字符编码(encoding="utf-8")。< p="">

UTF-8 也是 HTML5, CSS, JavaScript, PHP, 和 SQL 的默认编码。


所有的 XML 元素都必须有一个关闭标签

在 HTML 中,某些元素不必有一个关闭标签:

<p>This is a paragraph.
<br>

在 XML 中,省略关闭标签是非法的。所有元素都必须有关闭标签:

<p>This is a paragraph.</p>
<br />

注释:从上面的实例中,您也许已经注意到 XML 声明没有关闭标签。这不是错误。声明不是 XML 文档本身的一部分,它没有关闭标签。


XML 标签对大小写敏感

XML 标签对大小写敏感。标签 <Letter> 与标签 <letter> 是不同的。

必须使用相同的大小写来编写打开标签和关闭标签:

<Message>这是错误的</message>
<message>这是正确的</message>

注释:打开标签和关闭标签通常被称为开始标签和结束标签。不论您喜欢哪种术语,它们的概念都是相同的。


XML 必须正确嵌套

在 HTML 中,常会看到没有正确嵌套的元素:

<b><i>This text is bold and italic</b></i>

在 XML 中,所有元素都必须彼此正确地嵌套:

<b><i>This text is bold and italic</i></b>

在上面的实例中,正确嵌套的意思是:由于 <i> 元素是在 <b> 元素内打开的,那么它必须在 <b> 元素内关闭。



XML 属性值必须加引号

与 HTML 类似,XML 元素也可拥有属性(名称/值的对)。

在 XML 中,XML 的属性值必须加引号。

请研究下面的两个 XML 文档。 第一个是错误的,第二个是正确的:

<note date=12/11/2007>
<to>Tove</to>
<from>Jani</from>
</note>
<note date="12/11/2007">
<to>Tove</to>
<from>Jani</from>
</note>

在第一个文档中的错误是,note 元素中的 date 属性没有加引号。


实体引用

在 XML 中,一些字符拥有特殊的意义。

如果您把字符 "<" 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。

这样会产生 XML 错误:

<message>if salary < 1000 then</message>

为了避免这个错误,请用实体引用来代替 "<" 字符:

<message>if salary &lt; 1000 then</message>

在 XML 中,有 5 个预定义的实体引用:

&lt; < less than
&gt; > greater than
&amp; & ampersand
&apos; ' apostrophe
&quot; " quotation mark

注释:在 XML 中,只有字符 "<" 和 "&" 确实是非法的。大于号是合法的,但是用实体引用来代替它是一个好习惯。


XML 中的注释

在 XML 中编写注释的语法与 HTML 的语法很相似。

<!-- This is a comment -->

在 XML 中,空格会被保留

HTML 会把多个连续的空格字符裁减(合并)为一个:

HTML: Hello Tove
Output: Hello Tove

在 XML 中,文档中的空格不会被删减。


XML 以 LF 存储换行

在 Windows 应用程序中,换行通常以一对字符来存储:回车符(CR)和换行符(LF)。

在 Unix 和 Mac OSX 中,使用 LF 来存储新行。

在旧的 Mac 系统中,使用 CR 来存储新行。

XML 以 LF 存储换行。

XML 元素


XML 文档包含 XML 元素。


什么是 XML 元素?

XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。

一个元素可以包含:

  • 其他元素
  • 文本
  • 属性
  • 或混合以上所有...
<bookstore> <book category="CHILDREN"> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="WEB"> <title>Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore>

在上面的实例中,<bookstore> 和 <book> 都有 元素内容,因为他们包含其他元素。<book> 元素也有属性(category="CHILDREN")。<title>、<author>、<year> 和 <price> 有文本内容,因为他们包含文本。


XML 命名规则

XML 元素必须遵循以下命名规则:

  • 名称可以包含字母、数字以及其他的字符
  • 名称不能以数字或者标点符号开始
  • 名称不能以字母 xml(或者 XML、Xml 等等)开始
  • 名称不能包含空格

可使用任何名称,没有保留的字词。


最佳命名习惯

使名称具有描述性。使用下划线的名称也很不错:<first_name>、<last_name>。

名称应简短和简单,比如:<book_title>,而不是:<the_title_of_the_book>。

避免 "-" 字符。如果您按照这样的方式进行命名:"first-name",一些软件会认为您想要从 first 里边减去 name。

避免 "." 字符。如果您按照这样的方式进行命名:"first.name",一些软件会认为 "name" 是对象 "first" 的属性。

避免 ":" 字符。冒号会被转换为命名空间来使用(稍后介绍)。

XML 文档经常有一个对应的数据库,其中的字段会对应 XML 文档中的元素。有一个实用的经验,即使用数据库的命名规则来命名 XML 文档中的元素。

在 XML 中,éòá 等非英语字母是完全合法的,不过需要留意,您的软件供应商不支持这些字符时可能出现的问题。


XML 元素是可扩展的

XML 元素是可扩展,以携带更多的信息。

请看下面的 XML 实例:

<note> <to>Tove</to> <from>Jani</from> <body>Don't forget me this weekend!</body> </note>

让我们设想一下,我们创建了一个应用程序,可将 <to>、<from> 以及 <body> 元素从 XML 文档中提取出来,并产生以下的输出:

MESSAGE

To: Tove
From: Jani

Don't forget me this weekend!

想象一下,XML 文档的作者添加的一些额外信息:

<note> <date>2008-01-10</date> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>

那么这个应用程序会中断或崩溃吗?

不会。这个应用程序仍然可以找到 XML 文档中的 <to>、<from> 以及 <body> 元素,并产生同样的输出。

XML 的优势之一,就是可以在不中断应用程序的情况下进行扩展。

XML 属性


XML元素具有属性,类似 HTML。

属性(Attribute)提供有关元素的额外信息。


XML 属性

在 HTML 中,属性提供有关元素的额外信息:

<img src="computer.gif">
<a href="demo.html">

属性通常提供不属于数据组成部分的信息。在下面的实例中,文件类型与数据无关,但是对需要处理这个元素的软件来说却很重要:

<file type="gif">computer.gif</file>

XML 属性必须加引号

属性值必须被引号包围,不过单引号和双引号均可使用。比如一个人的性别,person 元素可以这样写:

<person sex="female">

或者这样也可以:

<person sex='female'>

如果属性值本身包含双引号,您可以使用单引号,就像这个实例:

<gangster name='George "Shotgun" Ziegler'>

或者您可以使用字符实体:

<gangster name="George &quot;Shotgun&quot; Ziegler">

XML 元素 vs. 属性

请看这些实例:

<person sex="female">
<firstname>Anna</firstname>
<lastname>Smith</lastname>
</person>
<person>
<sex>female</sex>
<firstname>Anna</firstname>
<lastname>Smith</lastname>
</person>

在第一个实例中,sex 是一个属性。在第二个实例中,sex 是一个元素。这两个实例都提供相同的信息。

没有什么规矩可以告诉我们什么时候该使用属性,而什么时候该使用元素。我的经验是在 HTML 中,属性用起来很便利,但是在 XML 中,您应该尽量避免使用属性。如果信息感觉起来很像数据,那么请使用元素吧。


我最喜欢的方式

下面的三个 XML 文档包含完全相同的信息:

第一个实例中使用了 date 属性:

<note date="10/01/2008">
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

第二个实例中使用了 date 元素:

<note>
<date>10/01/2008</date>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

第三个实例中使用了扩展的 date 元素(这是我的最爱):

<note>
<date>
<day>10</day>
<month>01</month>
<year>2008</year>
</date>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

避免 XML 属性?

因使用属性而引起的一些问题:

  • 属性不能包含多个值(元素可以)
  • 属性不能包含树结构(元素可以)
  • 属性不容易扩展(为未来的变化)

属性难以阅读和维护。请尽量使用元素来描述数据。而仅仅使用属性来提供与数据无关的信息。

不要做这样的蠢事(这不是 XML 应该被使用的方式):

<note day="10" month="01" year="2008"
to="Tove" from="Jani" heading="Reminder"
body="Don't forget me this weekend!">
</note>

针对元数据的 XML 属性

有时候会向元素分配 ID 引用。这些 ID 索引可用于标识 XML 元素,它起作用的方式与 HTML 中 id 属性是一样的。这个实例向我们演示了这种情况:

<messages>
<note id="501">
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
<note id="502">
<to>Jani</to>
<from>Tove</from>
<heading>Re: Reminder</heading>
<body>I will not</body>
</note>
</messages>

上面的 id 属性仅仅是一个标识符,用于标识不同的便签。它并不是便签数据的组成部分。

在此我们极力向您传递的理念是:元数据(有关数据的数据)应当存储为属性,而数据本身应当存储为元素。

XML 验证


拥有正确语法的 XML 被称为"形式良好"的 XML。

通过 DTD 验证的XML是"合法"的 XML。


形式良好的 XML 文档

"形式良好"的 XML 文档拥有正确的语法。

在前面的章节描述的语法规则:

  • XML 文档必须有一个根元素
  • XML元素都必须有一个关闭标签
  • XML 标签对大小写敏感
  • XML 元素必须被正确的嵌套
  • XML 属性值必须加引号
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

验证 XML 文档

合法的 XML 文档是"形式良好"的 XML 文档,这也符合文档类型定义(DTD)的规则:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE note SYSTEM "Note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

在上面的实例中,DOCTYPE 声明是对外部 DTD 文件的引用。下面的段落展示了这个文件的内容。


XML DTD

DTD 的目的是定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构:

<!DOCTYPE note
[
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>

如果您想要学习 DTD,请在我们的首页查找 DTD 教程。


XML Schema

W3C 支持一种基于 XML 的 DTD 代替者,它名为 XML Schema:

<xs:element name="note">

<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>

</xs:element>

http://www.runoob.com/xml/xml-tutorial.html

使用xml.dom解析xml

文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。

一个 DOM 的解析器在解析一个 XML 文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后你可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入xml文件。

python中用xml.dom.minidom来解析xml文件,实例如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

from xml.dom.minidom import parse
import xml.dom.minidom

# 使用minidom解析器打开 XML 文档
DOMTree = xml.dom.minidom.parse("movies.xml")
collection = DOMTree.documentElement
if collection.hasAttribute("shelf"):
   print "Root element : %s" % collection.getAttribute("shelf")

# 在集合中获取所有电影
movies = collection.getElementsByTagName("movie")

# 打印每部电影的详细信息
for movie in movies:
   print "*****Movie*****"
   if movie.hasAttribute("title"):
      print "Title: %s" % movie.getAttribute("title")

   type = movie.getElementsByTagName('type')[0]
   print "Type: %s" % type.childNodes[0].data
   format = movie.getElementsByTagName('format')[0]
   print "Format: %s" % format.childNodes[0].data
   rating = movie.getElementsByTagName('rating')[0]
   print "Rating: %s" % rating.childNodes[0].data
   description = movie.getElementsByTagName('description')[0]
   print "Description: %s" % description.childNodes[0].data

以上程序执行结果如下:

Root element : New Arrivals
*****Movie*****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Rating: PG
Description: Talk about a US-Japan war
*****Movie*****
Title: Transformers
Type: Anime, Science Fiction
Format: DVD
Rating: R
Description: A schientific fiction
*****Movie*****
Title: Trigun
Type: Anime, Action
Format: DVD
Rating: PG
Description: Vash the Stampede!
*****Movie*****
Title: Ishtar
Type: Comedy
Format: VHS
Rating: PG
Description: Viewable boredom

用 ElementTree 在 Python 中解析 XML

原文: http://eli.thegreenplace.net/2012/03/15/processing-xml-in-python-with-elementtree/

译者: TheLover_Z

当你需要解析和处理 XML 的时候,Python 表现出了它 “batteries included” 的一面。 标准库 中大量可用的模块和工具足以应对 Python 或者是 XML 的新手。

几个月前在 Python 核心开发者之间发生了一场 有趣的讨论 ,他们讨论了 Python 下可用的 XML 处理工具的优点,还有如何将它们最好的展示给用户看。这篇文章是我本人的拙作,我打算讲讲哪些工具比较好用还有为什么它们好用,当然,这篇文章也可以当作一个如何使用的基础教程来看。

这篇文章所使用的代码基于 Python 2.7,你稍微改动一下就可以在 Python 3.x 上面使用了。

应该使用哪个 XML 库?

Python 有非常非常多的工具来处理 XML。在这个部分我想对 Python 所提供的包进行一个简单的浏览,并且解释为什么 ElementTree 是你最应该用的那一个。

xml.dom.* 模块 - 是 W3C DOM API 的实现。如果你有处理 DOM API 的需要,那么这个模块适合你。注意:在 xml.dom 包里面有许多模块,注意它们之间的不同。

xml.sax.* 模块 - 是 SAX API 的实现。这个模块牺牲了便捷性来换取速度和内存占用。SAX 是一个基于事件的 API,这就意味着它可以“在空中”(on the fly)处理庞大数量的的文档,不用完全加载进内存(见注释1)。

xml.parser.expat - 是一个直接的,低级一点的基于 C 的 expat 的语法分析器(见注释2)。 expat 接口基于事件反馈,有点像 SAX 但又不太像,因为它的接口并不是完全规范于 expat 库的。

最后,我们来看看 xml.etree.ElementTree (以下简称 ET)。它提供了轻量级的 Python 式的 API ,它由一个 C 实现来提供。相对于 DOM 来说,ET 快了很多(见注释3)而且有很多令人愉悦的 API 可以使用。相对于 SAX 来说,ET 也有 ET.iterparse 提供了 “在空中” 的处理方式,没有必要加载整个文档到内存。ET 的性能的平均值和 SAX 差不多,但是 API 的效率更高一点而且使用起来很方便。我一会儿会给你们看演示。

我的建议 是尽可能的使用 ET 来处理 XML ,除非你有什么非常特别的需要。

ElementTree - 一个 API ,两种实现

ElementTree 生来就是为了处理 XML ,它在 Python 标准库中有两种实现。一种是纯 Python 实现例如 xml.etree.ElementTree ,另外一种是速度快一点的 xml.etree.cElementTree 。你要记住: 尽量使用 C 语言实现的那种,因为它速度更快,而且消耗的内存更少。如果你的电脑上没有 _elementtree (见注释4) 那么你需要这样做:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

这是一个让 Python 不同的库使用相同 API 的一个比较常用的办法。还是那句话,你的编译环境和别人的很可能不一样,所以这样做可以防止一些莫名其妙的小问题。注意:从 Python 3.3 开始,你没有必要这么做了,因为 ElementTree 模块会自动寻找可用的 C 库来加快速度。所以只需要 import xml.etree.ElementTree 就可以了。但是在 3.3 正式推出之前,你最好还是使用我上面提供的那段代码。

将 XML 解析为树的形式

我们来讲点基础的。XML 是一种分级的数据形式,所以最自然的表示方法是将它表示为一棵树。ET 有两个对象来实现这个目的 - ElementTree 将整个 XML 解析为一棵树, Element 将单个结点解析为树。如果是整个文档级别的操作(比如说读,写,找到一些有趣的元素)通常用 ElementTree 。单个 XML 元素和它的子元素通常用 Element 。下面的例子能说明我刚才啰嗦的一大堆。(见注释5)

我们用这个 XML 文件来做例子:

<?xml version="1.0"?>
<doc>
    <branch name="testing" hash="1cdf045c">
        text,source
    </branch>
    <branch name="release01" hash="f200013e">
        <sub-branch name="subrelease01">
            xml,sgml
        </sub-branch>
    </branch>
    <branch name="invalid">
    </branch>
</doc>

让我们加载并且解析这个 XML :

>>> import xml.etree.cElementTree as ET
>>> tree = ET.ElementTree(file='doc1.xml')

然后抓根结点元素:

>>> tree.getroot()
<Element 'doc' at 0x11eb780>

和预期一样,root 是一个 Element 元素。我们可以来看看:

>>> root = tree.getroot()
>>> root.tag, root.attrib
('doc', {})

看吧,根元素没有任何状态(见注释6)。就像任何 Element 一样,它可以找到自己的子结点:

>>> for child_of_root in root:
...   print child_of_root.tag, child_of_root.attrib
...
branch {'hash': '1cdf045c', 'name': 'testing'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}

我们也可以进入一个指定的子结点:

>>> root[0].tag, root[0].text
('branch', '
        text,source
    ')

找到我们感兴趣的元素

从上面的例子我们可以轻而易举的看到,我们可以用一个简单的递归获取 XML 中的任何元素。然而,因为这个操作比较普遍,ET 提供了一些有用的工具来简化操作.

Element 对象有一个 iter 方法可以对子结点进行深度优先遍历。 ElementTree 对象也有 iter 方法来提供便利。

>>> for elem in tree.iter():
...   print elem.tag, elem.attrib
...
doc {}
branch {'hash': '1cdf045c', 'name': 'testing'}
branch {'hash': 'f200013e', 'name': 'release01'}
sub-branch {'name': 'subrelease01'}
branch {'name': 'invalid'}

遍历所有的元素,然后检验有没有你想要的。ET 可以让这个过程更便捷。 iter 方法接受一个标签名字,然后只遍历那些有指定标签的元素:

>>> for elem in tree.iter(tag='branch'):
...   print elem.tag, elem.attrib
...
branch {'hash': '1cdf045c', 'name': 'testing'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}

来自 XPath 的帮助

为了寻找我们感兴趣的元素,一个更加有效的办法是使用 XPath 支持。 Element 有一些关于寻找的方法可以接受 XPath 作为参数。 find 返回第一个匹配的子元素, findall 以列表的形式返回所有匹配的子元素, iterfind 为所有匹配项提供迭代器。这些方法在 ElementTree 里面也有。

给出一个例子:

>>> for elem in tree.iterfind('branch/sub-branch'):
...   print elem.tag, elem.attrib
...
sub-branch {'name': 'subrelease01'}

这个例子在 branch 下面找到所有标签为 sub-branch 的元素。然后给出如何找到所有的 branch 元素,用一个指定 name 的状态即可:

>>> for elem in tree.iterfind('branch[@name="release01"]'):
...   print elem.tag, elem.attrib
...
branch {'hash': 'f200013e', 'name': 'release01'}

想要深入学习 XPath 的话,请看 这里 。

建立 XML 文档

ET 提供了建立 XML 文档和写入文件的便捷方式。 ElementTree 对象提供了 write 方法。

现在,这儿有两个常用的写 XML 文档的脚本。

修改文档可以使用 Element 对象的方法:

>>> root = tree.getroot()
>>> del root[2]
>>> root[0].set('foo', 'bar')
>>> for subelem in root:
...   print subelem.tag, subelem.attrib
...
branch {'foo': 'bar', 'hash': '1cdf045c', 'name': 'testing'}
branch {'hash': 'f200013e', 'name': 'release01'}

我们在这里删除了根元素的第三个子结点,然后为第一个子结点增加新状态。然后这个树可以写回到文件中。

>>> import sys
>>> tree.write(sys.stdout)   # ET.dump can also serve this purpose
<doc>
    <branch foo="bar" hash="1cdf045c" name="testing">
        text,source
    </branch>
<branch hash="f200013e" name="release01">
    <sub-branch name="subrelease01">
        xml,sgml
    </sub-branch>
</branch>
</doc>

注意状态的顺序和原文档的顺序不太一样。这是因为 ET 讲状态保存在无序的字典中。语义上来说,XML 并不关心顺序。

建立一个全新的元素也很容易。ET 模块提供了 SubElement 函数来简化过程:

>>> a = ET.Element('elem')
>>> c = ET.SubElement(a, 'child1')
>>> c.text = "some text"
>>> d = ET.SubElement(a, 'child2')
>>> b = ET.Element('elem_b')
>>> root = ET.Element('root')
>>> root.extend((a, b))
>>> tree = ET.ElementTree(root)
>>> tree.write(sys.stdout)
<root><elem><child1>some text</child1><child2 /></elem><elem_b /></root>

使用 iterparse 来处理 XML 流

就像我在文章一开头提到的那样,XML 文档通常比较大,所以将它们全部读入内存的库可能会有点儿小问题。这也是为什么我建议使用 SAX API 来替代 DOM 。

我们刚讲过如何使用 ET 来将 XML 读入内存并且处理。但它就不会碰到和 DOM 一样的内存问题么?当然会。这也是为什么这个包提供一个特殊的工具,用来处理大型文档,并且解决了内存问题,这个工具叫 iterparse 。

我给大家演示一个 iterparse 如何使用的例子。我用 自动生成 拿到了一个 XML 文档来进行说明。这只是开头的一小部分:

<?xml version="1.0" standalone="yes"?>
<site>
    <regions>
        <africa>
            <item id="item0">
                <location>United States</location>    <!-- Counting locations -->
                <quantity>1</quantity>
                <name>duteous nine eighteen </name>
                <payment>Creditcard</payment>
                <description>
                    <parlist>
[...]

我已经用注释标出了我要处理的元素,我们用一个简单的脚本来计数有多少 location 元素并且文本内容为“Zimbabwe”。这是用 ET.parse 的一个标准的写法:

tree = ET.parse(sys.argv[2])

count = 0
for elem in tree.iter(tag='location'):
    if elem.text == 'Zimbabwe':
        count += 1
print count

所有 XML 树中的元素都会被检验。当处理一个大约 100MB 的 XML 文件时,占用的内存大约是 560MB ,耗时 2.9 秒。

注意:我们并不需要在内存中加载整颗树。它检测我们需要的带特定值的 location 元素。其他元素被丢弃。这是 iterparse 的来源:

count = 0
for event, elem in ET.iterparse(sys.argv[2]):
    if event == 'end':
        if elem.tag == 'location' and elem.text == 'Zimbabwe':
            count += 1
    elem.clear() # discard the element

print count

这个循环遍历 iterparse 事件,检测“闭合的”(end)事件并且寻找 location 标签和指定的值。在这里 elem.clear() 是关键 - iterparse 仍然建立一棵树,只不过不需要全部加载进内存,这样做可以有效的利用内存空间(见注释7)。

处理同样的文件,这个脚本占用内存只需要仅仅的 7MB ,耗时 2.5 秒。速度的提升归功于生成树的时候只遍历一次。相比较来说, parse 方法首先建立了整个树,然后再次遍历来寻找我们需要的元素(所以慢了一点)。

结论

在 Python 众多处理 XML 的模块中, ElementTree 真是屌爆了。它将轻量,符合 Python 哲学的 API ,出色的性能完美的结合在了一起。所以说如果要处理 XML ,果断地使用它吧!

这篇文章简略地谈了谈 ET 。我希望这篇拙作可以抛砖引玉。

注释

注释1:和 DOM 不一样,DOM 将整个 XML 加载进内存并且允许随机访问任何深度地元素。

注释2: expat 是一个开源的用于处理 XML 的 C 语言库。Python 将它融合进自身。

注释3:Fredrik Lundh,是 ElementTree 的原作者,他提到了一些 基准 。

注释4:当我提到 _elementtree 的时候,我意思是 C 语言的 cElementTree._elementtree 扩展模块。

注释5:确定你手边有 模块手册 然后可以随时查阅我提到的方法和函数。

注释6: 状态 是一个意义太多的术语。Python 对象有状态,XML 元素也有状态。希望我能将它们表达的更清楚一点。

注释7:准确来说,树的根元素仍然存活。在某些情况下根结点非常大,你也可以丢弃它,但那需要多一点点代码。

Python基于DOM的XML编程接口

Python自带支持XML DOM的模块:xml.dom和xml.dom.minidom。

 
模块包含的函数
getDOMImplementation( )   
返回一个DOMImplementation对象,由该对象的createDocument( )方法创建一个DOM对象。
代码示例:
    from xml.dom.minidom import getDOMImplementation
    impl = getDOMImplementation()
    newdoc = impl.createDocument(None, "some_tag", None)
    top_element = newdoc.documentElement
    text = newdoc.createTextNode('Some textual content.')
    top_element.a(text)
 
DOMImplementation对象
DOMImplementation对象中的createDocument()方法创建并返回一个Document对象,语法:
createDocument(namespaceUriqualifiedNamedoctype)
前两个命令参数用于指定命名空间、顶级标签,可以都是None, 表示没有任何文档元素。参数doctype必须是由createDocumentType()方法创建的DocumentType对象,或是None。
 
DOMImplementation对象中的createDocumentType()方法创建并返回一个DocumentType对象,语法:
createDocumentType(qualifiedNamepublicIdsystemId)
返回的DocumentType对象由参数qualifiedName, publicId和systemId字串封装。
 
从外部文件创建DOM对象
代码示例:
    from xml.dom.minidom import parse, parseString
    dom1 = parse('c:\temp\mydata.xml')      # 直接指定XML文件名创建DOM对象。
    datasource = open('c:\temp\mydata.xml')   
    dom2 = parse(datasource)                          # 由一个已打开的文件句柄创建DOM对象。
    dom3 = parseString('< myxml > ... < /myxml >')     #从XML描述字符串创建DOM对象。
    assert dom3.documentElement.tagName == "myxml"
 
minidom中Node对象特有的方法
Python的DOM API说明主要包含在“xml.dom”模块文档中, “xml.dom.minidom”有如下一些特有的方法:
(1)Node.unlink( )
断开对象的所有引用,通过Python垃圾回收机制,对象所占用的内存空间能够被尽快释放。该方法主要用在Document对象,但也可以用于其下级子对象,释放某个子对象的内存空间。
(2)Node.toxml([encoding])
返回XML DOM对象的可显示字符串。如果参数缺省,XML头中将不指定字符集,此时默认字符集如不能显示所有字符,将选用Unicode字符集。"utf-8"是XML的缺省字符集。
(3)Node.toprettyxml([indent=""[, newl=""[, encoding=""]]])
返回进行过优化处理的可显示字符串。参数缺省情况下:indent为制表符(tabulator);newl为新行符( )。
 
Node对象
XML文档的所有组件都可以看作是Node对象。
Node.nodeType    用一个整数表示Node对象的类型。该属性为只读。
Node.parentNode    当前对象的双亲对象,对于Document对象该属性为None,对于Attr对象总是None。 该属性只读。
Node.attributes    包含的属性对象,只有节点对象的类型为元素是才有实际的值。该属性只读。
Node.previousSibling    与当前节点同属一个双亲节点的前一个紧邻的节点。对于第一个节点,该属性为
None。 该属性只读。
Node.nextSibling    与当前节点同属一个双亲节点的后一个紧邻的节点。对于最后一个节点,该属性为None。 该属性只读。
Node.childNodes    一个包含当前节点的子节点的列表。该属性只读。
Node.firstChild    第一个子节点,可以是None。 该属性只读。
Node.lastChild    最后一个子节点,可以是None。 该属性只读。
Node.localName    本地标签名称,完整名称中如有冒号,则为冒号之后的部分,否则为完整名称。该属性为字符串。
Node.prefix    名称前缀,完整名称中如有冒号,则为冒号之前的部分,否则为空。该属性为字符串,或None。
Node.namespaceURI    与元素关联的命名空间,为一字符串或None。该属性只读。
Node.nodeName    对于元素节点,等同于tagName;对于属性节点,等同于name。该节点只读。
Node.nodeValue    与nodeName类似,对于不同类型的节点,有不同的含义。该属性只读。
Node.hasAttributes()    如果节点包含有属性,返回True。
Node.hasChildNodes()    如果节点包含子节点,返回True。
Node.isSameNode(other)    如果指定的节点对象引用的就是本节点,该方法返回True。 
Node.append Child(newChild)    在当前节点的最后一个子节点后增加一个新的子节点。该方法将返回新子节点。 
Node.insertBefore(newChildrefChild)    在指定的子节点前插入一个新的节点,如果参照的节点为None,新节点将插入到子节点列表的末尾。该方法返回新子节点。 
Node.removeChild(oldChild)    删除一个子节点,该方法执行成功后返回被删除的节点,应适用unlink()方法释放被删除节点占用的内存。  
Node.replaceChild(newChildoldChild)    用一个新的节点替换一个已经存在的子节点。
Node.normalize()    将邻接的文本节点(textNode)连接为单个文本实例,以简化对文本的处理。
Node.cloneNode(deep)    克隆当前节点,设置deep表示克隆所有的子节点。该方法返回克隆节点。 
 
NodeList对象
NodeList是一个有序的对象列表。它通常用来表示一个节点的子节点列表,或执行getElementsByTagName_r()和getElementsByTagNameNS()方法返回的对象列表。
NodeList.item(i)    返回序列中的第i各元素,如果不存在返回None。
NodeList.length    序列中节点的数量。
 
DocumentType对象
Document对象的doctype属性是一个DocumentType对象。 它是一类特殊的节点,有如下属性:
DocumentType.publicId    文档类型定义外部子集中的公共标识符,可以是一个字符串,或None。
DocumentType.systemId    文档类型定义外部子集中的系统标识符,可以是一个URI字符串,或None。
DocumentType.internalSubset
DocumentType.name    如果存在,用来声明根元素的名称。
DocumentType.entities
DocumentType.notations
 
 Document对象
Document代表整个XML文档,由getDOMImplementation()创建,它从Node继承属性,nodeType值为9。
 
Document.documentElement    Document对象的根元素。
 
document.  create Element(tagName)    建立并返回一个新元素节点,但要加入的节点树中,需使用insertBefore() 或 append Child()方法。
document.  create ElementNS(namespaceURItagName)    
 
document.  create TextNode(data)    以参数传入的数据建立并返回一个文本节点,但未加入到节点树中。 
 
document.  create Comment(data)    以参数传入的数据建立并返回一个注释节点,但未加入到节点树中。
 
document.  create ProcessingInstruction(targetdata)    
 
document.  create Attribute(name)    建立并返回一个属性节点,但要关联到一个元素节点,需要使用方法setAttributeNode()。
document.  create AttributeNS(namespaceURIqualifiedName)   
 
Document.getElementsByTagName  (tagName)    在所有后代节点中查找指定tagname的元素节点,返回子元素到列表,注意是列表。
Document.getElementsByTagNameNS(namespaceURIlocalName)    
 
Element对象
Element是Node的子类,nodeType值为1。
Element.tagName    元素类型名称。 
Element.getElementsByTagName  (tagName)    与Document类相同。
Element.getElementsByTagNameNS(namespaceURIlocalName)
Element.hasAttribute(name)  
Element.hasAttributeNS(namespaceURIlocalName)
Element.getAttribute(name)    
Element.getAttributeNode(attrname)    返回属性节点。
Element.getAttributeNS(namespaceURIlocalName)
Element.getAttributeNodeNS(namespaceURIlocalName)
Element.removeAttribute(name)
Element.removeAttributeNode(oldAttr)
Element.removeAttributeNS(namespaceURIlocalName)
Element.setAttribute(namevalue)
Element.setAttributeNode(newAttr)
Element.setAttributeNodeNS(newAttr)
Element.setAttributeNS(namespaceURIqnamevalue)
 
Attr对象
Attr继承自Node。 nodeType值为2。
Attr.name    属性名称,存在命名空间时,名称中包含冒号。
Attr.localName   存在命名空间时,为冒号后的部分。
Attr.prefix    存在命名空间时,为冒号前的部分。
Attr.value    文本类型的属性值,与nodeValue同义。
 
NamedNodeMap对象
NamedNodeMap继承自Node。
NamedNodeMap.length    属性列表的长度。
NamedNodeMap.item(index)    返回指定索引的属性项,通过value属性获得属性值。
 
Comment对象
表示XML文档中的注释,是Node的子类,本身不能有自节点。
Comment.data    注释文本字符串。
 
Text和CDATASection对象
Text接口表示XML文档中的文本,它不存在子节点。如果支持XML扩展,CDATA标记中的文本将被保存在CDATASection对象中。Text节点到nodeType值为3。
Text.data    节点的文本字符串。
 
ProcessingInstruction对象
表示XML文档中的处理指定,它不存在子节点。
ProcessingInstruction.target    处理指令到出现的第一个空格为止的内容,是只读属性。
ProcessingInstruction.data    处理指令出现的第一个空格字符之后的内容。
 
异常类
DOM级别2 推荐定义单一的异常类DOMException作为基类,并通过定义一系列的常量为可能发生的错误分类。DOMException实例的code属性用来存储表示错误类型的值。异常类型常量对应的派生异常类如下:
    DOMSTRING_SIZE_ERR DomstringSizeErr
    HIERARCHY_REQUEST_ERR HierarchyRequestErr
    INDEX_SIZE_ERR IndexSizeErr
    INUSE_ATTRIBUTE_ERR InuseAttributeErr
    INVALID_ACCESS_ERR InvalidAccessErr
    INVALID_CHARACTER_ERR InvalidCharacterErr
    INVALID_MODIFICATION_ERR InvalidModificationErr
    INVALID_STATE_ERR InvalidStateErr
    NAMESPACE_ERR NamespaceErr
    NOT_FOUND_ERR NotFoundErr
    NOT_SUPPORTED_ERR NotSupportedErr
    NO_DATA_ALLOWED_ERR NoDataAllowedErr
    NO_MODIFICATION_ALLOWED_ERR NoModificationAllowedErr
    SYNTAX_ERR SyntaxErr
    WRONG_DOCUMENT_ERR WrongDocumentErr

 http://blog.sina.com.cn/s/blog_da4487c40102v3jx.html

使用minidom来处理XML的示例(Python 学习)(转载)

一.XML的读取.

在 NewEdit 中有代码片段的功能,代码片段分为片段的分类和片段的内容。在缺省情况下都是用XML格式保存的。下面我讲述一下,如何使用minidom来读取和保存XML文件。

下面是片段分类的一个示例文件--catalog.xml

<?xml version="1.0" encoding="utf-8"?>
<catalog>
    <maxid>4</maxid>
    <item id="1">
        <caption>Python</caption>
        <item id="4">
            <caption>测试</caption>
        </item>
    </item>
    <item id="2">
        <caption>Zope</caption>
    </item>
</catalog>

分类是树状结构,显示出来可能为:

Python
    测试
Zope

先简单介绍一下XML的知识,如果你已经知道了可以跳过去。

1. XML文档的编码

此XML文档的编码为utf-8,因此你看到的“测试”其实是UTF-8编码。在XML文档的处理中都是使用UTF-8编码进行的,因此,如果你不写明encoding的话,都是认为文件是UTF-8编码的。在Python中,好象只支持几种编码,象我们常用的GB2312码就不支持,因此建议大家在处理XML时使用UTF-8编码。

2. XML文档的结构

XML文档有XML头信息和XML信息体。头信息如:

<?xml version="1.0" encoding="utf-8"?>

它表明了此XML文档所用的版本,编码方式。有些复杂的还有一些文档类型的定义(DOCTYPE),用于定义此XML文档所用的DTD或Schema和一些实体的定义。这里并没有用到,而且我也不是专家,就不再细说了。

XML信息体是由树状元素组成。每个XML文档都有一个文档元素,也就是树的根元素,所有其它的元素和内容都包含在根元素中。

3. DOM

DOM是Document Object Model的简称,它是以对象树来表示一个XML文档的方法,使用它的好处就是你可以非常灵活的在对象中进行遍历。

4. 元素和结点

元素就是标记,它是成对出现的。XML文档就是由元素组成的,但元素与元素之间可以有文本,元素的内容也是文本。在minidom中有许多的结点,元素也属于结点的一种,它不是叶子结点,即它存在子结点;还存在一些叶子结点,如文本结点,它下面不再有子结点。

象catalog.xml中,文档元素是catalog,它下面有两种元素:maxid和item。maxid用来表示当前最大的item的id值。每一个item都有一个id属性,id属性是唯一的,在 NewEdit 中用来生成每个分类所对应的代码片段的XML文档名,因此不能重复,而且它是一个递增的值。item元素有一个caption子元素,用来表示此分类项的名称,它还可以包含item元素。这样,就定义了一个树状XML结构,下面让我们看一看如果把它们读出来。


一、得到dom对象

>>> import xml.dom.minidom
>>> dom = xml.dom.minidom.parse('d:/catalog.xml')

这样我们得到了一个dom对象,它的第一个元素应该是catalog。

二、得到文档元素对象

>>> root = dom.documentElement

这样我们得到了根元素(catalog)。

三、结点属性

每一个结点都有它的nodeName,nodeValue,nodeType属性。nodeName为结点名字。

>>> root.nodeName
u'catalog'

nodeValue是结点的值,只对文本结点有效。nodeType是结点的类型,现在有以下几种:

'ATTRIBUTE_NODE'
'CDATA_SECTION_NODE'
'COMMENT_NODE'
'DOCUMENT_FRAGMENT_NODE'
'DOCUMENT_NODE'
'DOCUMENT_TYPE_NODE'
'ELEMENT_NODE'
'ENTITY_NODE'
'ENTITY_REFERENCE_NODE'
'NOTATION_NODE'
'PROCESSING_INSTRUCTION_NODE'
'TEXT_NODE'

这些结点通过名字很好理解。catalog是ELEMENT_NODE类型。

>>> root.nodeType
1
>>> root.ELEMENT_NODE
1

四、子元素、子结点的访问

访问子元素、子结点的方法很多,对于知道元素名字的子元素,可以使用getElementsByTagName方法,如读取maxid子元素:

>>> root.getElementsByTagName('maxid')
[<DOM Element: maxid at 0xb6d0a8>]

这样返回一个列表,由于我们的例子中maxid只有一项,因此列表也只有一项。

如果想得到某个元素下的所有子结点(包括元素),可以使用childNodes属性:

>>> root.childNodes
[<DOM Text node "     ">, <DOM Element: maxid at 0xb6d0a8>, <DOM Text node "     ">, <DOM Element: item at 0xb6d918>, <DOM Text node "     ">, <DOM Element: item at 0xb6de40>, <DOM Text node "     ">, <DOM Element: item at 0xb6dfa8>, <DOM Text node " ">]

可以看出所有两个标记间的内容都被视为文本结点。象每行后面的回车,都被看到文本结点。从上面的结果我们可以看出每个结点的类型,本例中有文本结点和元素结点;结点的名字(元素结点);结点的值(文本结点)。每个结点都是一个对象,不同的结点对象有不同的属性和方法,更详细的要参见文档。由于本例比较简单,只涉及文本结点和元素结点。

getElementsByTagName可以搜索当前元素的所有子元素,包括所有层次的子元素。childNodes只保存了当前元素的第一层子结点。

这样我们可以遍历childNodes来访问每一个结点,判断它的nodeType来得到不同的内容。如,打印出所有元素的名字:

>>> for node in root.childNodes:
    if node.nodeType == node.ELEMENT_NODE:
        print node.nodeName
        
maxid
item
item

对于文本结点,想得到它的文本内容可以使用: .data属性。

对于简单的元素,如:<caption>Python</caption>,我们可以编写这样一个函数来得到它的内容(这里为Python)。

def getTagText(root, tag):
    node = root.getElementsByTagName(tag)[0]
    rc = ""
    for node in node.childNodes:
        if node.nodeType in ( node.TEXT_NODE, node.CDATA_SECTION_NODE):
            rc = rc + node.data
    return rc

这个函数只处理找到的第一个符合的子元素。它会将符合的第一个子元素中的所有文本结点拼在一起。当nodeType为文本类结点时,node.data为文本的内容。如果我们考查一下元素caption,我们可能看到:

[<DOM Text node "Python">]

说明caption元素只有一个文本结点。

如果一个元素有属性,那么可以使用getAttribute方法,如:

>>> itemlist = root.getElementsByTagName('item')
>>> item = itemlist[0]
>>> item.getAttribute('id')
u'1'

这样就得到了第一个item元素的属性值。

下面让我们简单地小结一下如何使用minidom来读取XML中的信息

1. 导入xml.dom.minidom模块,生成dom对象
2. 得到文档对象(根对象)
3. 通过getElementsByTagName()方法和childNodes属性(还有其它一些方法和属性)找到要处理的元素
4. 取得元素下文本结点的内容


二.写入.

下面我来演示一下如何从无到有生成象catalog.xml一样的XML文件。

一、生成dom对象

>>> import xml.dom.minidom
>>> impl = xml.dom.minidom.getDOMImplementation()
>>> dom = impl.createDocument(None, 'catalog', None)

这样就生成了一个空的dom对象。其中catalog为文档元素名,即根元素名。

二、显示生成的XML内容

每一个dom结点对象(包括dom对象本身)都有输出XML内容的方法,如:toxml(), toprettyxml()

toxml()输出紧凑格式的XML文本,如:

<catalog><item>test</item><item>test</item></catalog>

toprettyxml()输出美化后的XML文本,如:

<catalog>
    <item>
        test
    </item>
    <item>
        test
    </item>
</catalog>

可以看出,它是将每个结点后面都加入了回车符,并且自动处理缩近。但对于每一个元素,如果元素只有文本内容,则我希望元素的tag与文本是在一起的,如:

<item>test</item>

而不想是分开的格式,但minidom本身是不支持这样的处理。关于如何实现形如:

<catalog>
    <item>test</item>
    <item>test</item>
</catalog>

这样的XML格式,后面我们再说。

三、生成各种结点对象

dom对象拥有各种生成结点的方法,下面列出文本结点,CDATA结点和元素结点的生成过程。

1. 文本结点的生成

>>> text=dom.createTextNode('test')
test

要注意的是,在生成结点时,minidom并不对文本字符进行检查,象文本中如果出现了'<','&'之类的字符,应该转换为相应的实体符号'&lt;','&amp;'才可以,这里没有做这个处理。

2. CDATA结点的生成

>>> data = dom.createCDATASection('aaaaaa bbbbbb')
>>> data.toxml()
'<![CDATA[aaaaaa bbbbbb]]>'

CDATA是用于包括大块文本,同时可以不用转换'<','&'字符的标记,它是用<![CDATA[文本]]>来包括的。但文本中不可以有"]]>"这样的串存在。生成结点时minidom不作这些检查,只有当你输出时才有可能发现有错。

3. 元素结点的生成

>>> item = dom.createElement('caption')
>>> item.toxml()
'<caption/>'

对于象元素这样的结点,生成的元素结点其实是一个空元素,即不包含任何文本,如果要包含文本或其它的元素,我们需要使用appendChild()或insertBefore()之类的方法将子结点加就到元素结点中。如将上面生成的text结点加入到caption元素结点中:

>>> item.appendChild(text)
<DOM Text node "test">
>>> item.toxml()
'<caption>test</caption>'

使用元素对象的setAttribute()方法可以向元素中加入属性,如:

>>> item.setAttribute('id', 'idvalue')
>>> item.toxml()
'<caption id="idvalue">test</caption>'

四、生成dom对象树

我们有了dom对象,又知道了如何生成各种结点,包括叶子结点(不包含其它结点的结点,如文本结点)和非叶子结点(包含其它结点的结点,如元素结点)的生成,然后就需要利用结点对象本身的appendChild()或insertBefore()方法将各个结点根据在树中的位置连起来,串成一棵树。最后要串到文档结点上,即根结点上。如一个完整的示例为:

>>> import xml.dom.minidom
>>> impl = xml.dom.minidom.getDOMImplementation()
>>> dom = impl.createDocument(None, 'catalog', None)
>>> root = dom.documentElement
>>> item = dom.createElement('item')
>>> text = dom.createTextNode('test')
>>> item.appendChild(text)
<DOM Text node "test">
>>> root.appendChild(item)
<DOM Element: item at 0xb9cf80>
>>> print root.toxml()
<catalog><item>test</item></catalog>

五、简单生成元素结点的函数

下面是我写的一个小函数,用于简单的生成类似于:

<caption>test</caption>

或形如:

<item><![CDATA[test]]></item>

的元素结点

1       def makeEasyTag(dom, tagname, value, type='text'):
2           tag = dom.createElement(tagname)
3           if value.find(']]>') > -1:
4               type = 'text'
5           if type == 'text':
6               value = value.replace('&', '&amp;')
7               value = value.replace('<', '&lt;')
8               text = dom.createTextNode(value)
9           elif type == 'cdata':
10              text = dom.createCDATASection(value)
11          tag.appendChild(text)
12          return tag

参数说明:

  • dom为dom对象
  • tagname为要生成元素的名字,如'item'
  • value为其文本内容,可以为多行
  • type为文本结点的格式,'text'为一般Text结点,'cdata'为CDATA结点

函数处理说明:

  • 首先创建元素结点
  • 查找文本内容是否有']]>',如果找到,则此文本结点只可以是Text结点
  • 如果结点类型为'text',则对文本内容中的'<'替换为'&lt;','&'替换为'&amp;',再生成文本结点
  • 如果结点类型为'cdata',则生成CDATA结点
  • 将生成的文本结点追加到元素结点上

因此这个小函数可以自动地处理字符转化及避免CDATA结点中出现']]>'串。

上面生成'item'结点的语句可以改为:

>>> item = makeEasyTag(dom, 'item', 'test')
>>> item.toxml()
'<item>test</item>'

六、写入到XML文件中

dom对象树已经生成好了,我们可以调用dom的writexml()方法来将内容写入文件中。writexml()方法语法格式为:

writexml(writer, indent, addindent, newl, encoding)

  • writer是文件对象
  • indent是每个tag前填充的字符,如:'  ',则表示每个tag前有两个空格
  • addindent是每个子结点的缩近字符
  • newl是每个tag后填充的字符,如:' ',则表示每个tag后面有一个回车
  • encoding是生成的XML信息头中的encoding属性值,在输出时minidom并不真正进行编码的处理,如果你保存的文本内容中有汉字,则需要自已进行编码转换。

writexml方法是除了writer参数必须要有外,其余可以省略。下面给出一个文本内容有汉字的示例:

1       >>> import xml.dom.minidom
2       >>> impl = xml.dom.minidom.getDOMImplementation()
3       >>> dom = impl.createDocument(None, 'catalog', None)
4       >>> root = dom.documentElement
5       >>> text = unicode('汉字示例', 'cp936')
6       >>> item = makeEasyTag(dom, 'item', text)
7       >>> root.appendChild(item)
8       <DOM Element: item at 0xb9ceb8>
9       >>> root.toxml()
10      u'<catalog><item>u6c49u5b57u793au4f8b</item></catalog>'
11      >>> f=file('d:/test.xml', 'w')
12      >>> import codecs
13      >>> writer = codecs.lookup('utf-8')[3](f)
14      >>> dom.writexml(writer, encoding='utf-8')
15      >>> writer.close()

5行 因为XML处理时内部使用Unicode编码,因此象汉字首先要转成Unicode,如果你不做这一步minicode并不检查,并且保存时可能不会出错。但读取时可能会出错。
12-13行 生成UTF-8编码的写入流对象,这样在保存时会自动将Unicode转换成UTF-8编码。

这样写XML文件就完成了。

三.美化.

对于dom对象的writexml()方法,虽然可以控制一些格式上的输出,但结果并不让人满意。比如我想实现:

<catalog>
    <item>test</item>
    <item>test</item>
</catalog>

而不是:

<catalog>
    <item>
        test
    </item>
    <item>
        test
    </item>
</catalog>

如果是象下面的输出结果我无法区分原来文本中是否带有空白,而上一种结果则不存在这一问题。好在我在wxPython自带的XML资源编辑器(xred)发现了美化的代码。代码如下:

1       def Indent(dom, node, indent = 0):
2           # Copy child list because it will change soon
3           children = node.childNodes[:]
4           # Main node doesn't need to be indented
5           if indent:
6               text = dom.createTextNode(' ' + ' ' * indent)
7               node.parentNode.insertBefore(text, node)
8           if children:
9               # Append newline after last child, except for text nodes
10              if children[-1].nodeType == node.ELEMENT_NODE:
11                  text = dom.createTextNode(' ' + ' ' * indent)
12                  node.appendChild(text)
13              # Indent children which are elements
14              for n in children:
15                  if n.nodeType == node.ELEMENT_NODE:
16                      Indent(dom, n, indent + 1)

参数说明:

dom为dom对象
node为要处理的元素结点
indent指明缩近的层数

函数说明:

Indent是一个递归函数,当一个结点有子元素时进行递归处理。主要是解决子元素的换行和缩近的处理。这里缩近是写死的,每一级缩近使用一个制表符。如果你愿意可以改为你想要的内容。就是把函数中的' '换替一下。或干脆写成一个全局变量,或参数以后改起来可能要容易的多。不过在 NewEdit 中,这样的处理足够了,就没有做这些工作。

Indent基本的想法就是递归遍历所有子结点,在所有需要加入回车和缩近的地方插入相应的文本结点。这样再使用writexml()输出时就是缩近好了的。具体程序不再细说,直接用就行了。

但这里要注意的是:

Indent()要修改原dom对象,因此在调用它之前最好先复制一个临时dom对象,使用完毕后再清除这个临时dom对象即可。下面是详细的调用过程:

1       domcopy = dom.cloneNode(True)
2       Indent(domcopy, domcopy.documentElement)
3       f = file(xmlfile, 'wb')
4       writer = codecs.lookup('utf-8')[3](f)
5       domcopy.writexml(writer, encoding = 'utf-8')
6       domcopy.unlink()

1行 克隆一个dom对象
2行 进行缩近处理
3-4行 进行UTF-8编码处理
5行 生成XML文件
6行 清除dom对象的内容

Python 使用 ElementTree 处理 XML

一、引用方法

ElementTree 所在文件保存在 Lib/xml/etree/ElementTree.py,所以我们通过下面的代码引用它,之后就可以使用 ET. 来访问 ElementTree 中的函数。

1
import xml.etree.ElementTree as ET

二、一个 XML 例子

下面所有的操作都将下面这段 XML 为例,我们将它保存为sample.xml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>

先对 XML 的格式做一些说明:

  • Tag: 使用 < 和 > 包围的部分,如 成为 start-tag,是 end-tags;
  • Element:被 Tag 包围的部分,如68,可以认为是一个节点,它可以有子节点;
  • Attribute:在 Tag 中可能存在的 name/value 对,如 中的 name=”Liechtenstein”,一般表示属性。

三、解析 XML

读入 XML 数据

首先读入 XML,有两种途径,从文件读入和从字符串读入。
从文件读入:

1
2
3
import xml.etree.ElementTree as ET
tree = ET.parse('sample.xml')
root = tree.getroot()

从字符串读入:

1
root = ET.fromstring(sample_as_string)

tree 和 root 分布是 ElementTree 中两个很重要的类的对象:

  • ElementTree
  • Element

查看 Tag 和 Attribute

这时得到的 root 是一个指向Element 对象,我们可以通过查看 root 的 tag 和 attrib 来验证这一点:

1
2
3
4
>>> root.tag
'data'
>>> root.attrib
{}

上面的代码说明了查看一个 Element 的 Tag 和 Attribute 的方法,Tag 是一个 字符串 ,而 Attribute 得到的是一个 字典

另外,还可以使用

  • Element.get(AttributeName)

来代替 Element.attrib[AttributeName]来访问。

查看孩子

root.attrib 返回的是一个空字典,如果看 root 的孩子,可以得到非空的 attrib 字典。

1、使用 for…in…访问

1
2
for child in root:
print child.tag, child.attrib

得到

country {‘name’: ‘Liechtenstein’}
country {‘name’: ‘Singapore’}
country {‘name’: ‘Panama’}

2、使用下标访问

如:

1
2
3
4
>>> print root[0].tag
country
>>> print root[0][0].tag
rank

3、使用 Tag 名称访问

下标访问的方法虽然简单,但是在未知 XML 具体结构的时候并不适用,通过 Tag 名称访问的方法更具有普适性。这里用到 Element 类的几个函数,分别是

  • Element.iter()
  • Element.findall()
  • Element.find()

这两个函数使用的场景有所差异:
Element.iter()用来寻找 所有 符合要求的 Tag,注意,这里查找的范围 是所有孩子和孩子的孩子 and so on。如果查看所有的 year,可以使用下面的代码:

1
2
for neighbor in root.iter('year'):
print neighbor.text

返回

2008
2011
2011

Element.findall()只查找 直接的孩子 ,返回所有符合要求的 Tag 的 Element,而Element.find() 只返回符合要求的第一个 Element。如果查看 Singapore 的 year 的值,可以使用下面的代码:

1
2
3
4
for country in root.findall('country'):
if country.attrib['name'] == 'Singapore':
year = country.find('year') # 使用 Element.find()
print year.text
1
2
3
4
for country in root.findall('country'):
if country.attrib['name'] == 'Singapore':
years = country.findall('year') # 使用 Element.findall()
print years[0].text # 注意和上段的区别

查看 Element 的值

我们可以直接用 Element.text 来得到这个 Element 的值。

四、修改 XML

前面已经介绍了如何获取一个 Element 的对象,以及查看它的 Tag、Attribute、值和它的孩子。下面介绍如何修改一个 Element 并对 XML 文件进行保存

修改 Element

修改 Element 可以直接访问 Element.text。
修改 Element 的 Attribute,也可以用来新增 Attribute:

Element.set(‘AttributeName’,’AttributeValue’)

新增孩子节点:

Element.append(childElement)

删除孩子节点:

Element.remove(childElement)

保存 XML

我们从文件解析的时候,我们用了一个 ElementTree 的对象 tree,在完成修改之后,还用 tree 来保存 XML 文件。

1
tree.write('output.xml')

构建 XML

ElementTree 提供了两个静态函数(直接用类名访问,这里我们用的是 ET)可以很方便的构建一个 XML,如:

1
2
3
4
5
6
7
root = ET.Element('data') 
country = ET.SubElement(root,'country', {'name':'Liechtenstein'})
rank = ET.SubElement(country,'rank')
rank.text = '1'
year = ET.SubElement(country,'year')
year.text = '2008'
ET.dump(root)

就可以得到

12008

五、XPath 支持

XPath 表达式用来在 XML 中定位 Element,下面给一个例子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import xml.etree.ElementTree as ET

root = ET.fromstring(countrydata)

# Top-level elements
root.findall(".")

# All 'neighbor' grand-children of 'country' children of the top-level
# elements
root.findall("./country/neighbor")

# Nodes with name='Singapore' that have a 'year' child
root.findall(".//year/..[@name='Singapore']")

# 'year' nodes that are children of nodes with name='Singapore'
root.findall(".//*[@name='Singapore']/year")

# All 'neighbor' nodes that are the second child of their parent
root.findall(".//neighbor[2]")

参考

ElementTree 主页
ElementTree 的函数与类介绍

ElementTree解析

两种实现

ElementTree生来就是为了处理XML ,它在python标准库中有两种实现。

一种是纯Python实现,例如: xml.etree.ElementTree 

另外一种是速度快一点的:   xml.etree.cElementTree

尽量使用C语言实现的那种,因为它速度更快,而且消耗的内存更少! 在程序中可以这样写:

  1. try:  
  2.     import xml.etree.cElementTree as ET  
  3. except ImportError:  
  4.     import xml.etree.ElementTree as ET  

常用方法

  1. # 当要获取属性值时,用attrib方法。  
  2. # 当要获取节点值时,用text方法。  
  3. # 当要获取节点名时,用tag方法。  

示例XML

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <info>  
  3.    <intro>Book message</intro>  
  4.     <list id='001'>  
  5.         <head>bookone</head>  
  6.         <name>python check</name>  
  7.         <number>001</number>  
  8.         <page>200</page>  
  9.     </list>  
  10.     <list id='002'>  
  11.         <head>booktwo</head>  
  12.         <name>python learn</name>  
  13.         <number>002</number>  
  14.         <page>300</page>  
  15.     </list>  
  16. </info>  

###########

##  加载XML 

###########
方法一:加载文件

  1. root = ET.parse('book.xml')  

方法二:加载字符串

  1. root = ET.fromstring(xmltext)  

###########

## 获取节点

###########
方法一:获得指定节点->getiterator()方法

  1. book_node = root.getiterator('list')  

方法二:获得指定节点->findall()方法

  1. book_node = root.findall('list')  

方法三:获得指定节点->find()方法

  1. book_node = root.find('list')  

方法四:获得儿子节点->getchildren()

  1. for node in book_node:  
  2.     book_node_child = node.getchildren()[0]  
  3.     print book_node_child.tag, '=> ', book_node_child.text  

###########

##  例子01 

###########

  1. # coding=utf-8  
  2.   
  3. try:                                           # 导入模块  
  4.     import xml.etree.cElementTree as ET  
  5. except ImportError:  
  6.     import xml.etree.ElementTree as ET  
  7.   
  8. root   = ET.parse('book.xml')                 # 分析XML文件  
  9. books  = root.findall('/list')                # 查找所有根目录下的list的子节点  
  10. for book_list in books:                       # 对查找后的结果遍历  
  11.     print "=" * 30                            # 输出格式             
  12.     for book in book_list:                    # 对每个子节点再进行遍历,找出里面你的属性及值                       
  13.         if book.attrib.has_key('id'):         # 一句id来做条件判断  
  14.             print "id:", book.attrib['id']    # 根据id打印出属性值  
  15.         print book.tag + '=> ' + book.text    # 输出标签及文本内容  
  16. print "=" * 30  

输出结果:

    1. ==============================  
    2. head=> bookone  
    3. name=> python check  
    4. number=> 001  
    5. page=> 200  
    6. ==============================  
    7. head=> booktwo  
    8. name=> python learn  
    9. number=> 002  
    10. page=> 300  
    11. ==============================  

Python 标准库之 xml.etree.ElementTree

 

简介

Element类型是一种灵活的容器对象,用于在内存中存储结构化数据。

[注意]xml.etree.ElementTree模块在应对恶意结构数据时显得并不安全。

每个element对象都具有以下属性:

  1. tag:string对象,表示数据代表的种类。

  2. attrib:dictionary对象,表示附有的属性。

  3. text:string对象,表示element的内容。

  4. tail:string对象,表示element闭合之后的尾迹。

  5. 若干子元素(child elements)。

<tag attrib1=1>text</tag>tail
1 2 3 4

创建元素的方法有Element或者SubElement(),前者称作元素的构建函数(constructor),用以构建任一独存的元素;后者称作元素的制造函数(factory function),用以制造某一元素的子元素。

有了一串元素之后,使用ElementTree类来将其打包,把一串元素转换为xml文件或者从xml文件中解析出来。

若想加快速度,可以使用C语言编译的API xml.etree.cElementTree。

复制代码
<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>
复制代码

XML操作

  • 读取

#从变量读取,参数为XML段,返回的是一个根Element对象
root = ET.fromstring(country_data_as_string)

#从xml文件中读取,用getroot获取根节点,根节点也是Element对象
tree = ET.parse('file.xml')
root = tree.getroot()
  • 访问

    • 访问Element对象的标签、属性和值
tag = element.tag
attrib = element.attrib
value = element.text
    • 访问子节点
#打印根节点的标签和属性,获取
for child in root:
    print(child.tag, child.attrib)
  • 查找操作

    • Element元素迭代子元素:Element.iter("tag"),可以罗列该节点所包含的所有其他节点(element对象)
#打印根节点中所有的neighbor对象的name属性
for neighbor in root.iter('neighbor'):
    print(neighbor.attrib['name'])
    • Element.findall("tag"):查找当前元素为“tag”的直接子元素
#findall只能用来查找直接子元素,不能用来查找rank,neighbor等element
for country in root.findall('country'):
    rank = country.find('rank').text
    name = country.find('rank').text
    neig = country.find('neighbor').attrib
    print(rank, name,neig)
    • Element.find("tag"):查找为tag的第一个直接子元素
#返回第一个tag为country的element,如没有,返回None
firstCountry = root.find("country")
print(firstCountry)
  • 创建xml文件

复制代码
__author__ = 'xua'

import xml.etree.ElementTree as ET
#创建根节点
a = ET.Element("root")
#创建子节点,并添加属性
b = ET.SubElement(a,"sub1")
b.attrib = {"name":"name attribute"}
#创建子节点,并添加数据
c = ET.SubElement(a,"sub2")
c.text = "test"

#创建elementtree对象,写文件
tree = ET.ElementTree(a)
tree.write("test.xml")
复制代码

创建的新文件内容为:<root><sub1 name="name attribute" /><sub2>test</sub2></root>

  • 修改XML文件

    • ElementTree.write("xmlfile"):更新xml文件
    • Element.append():为当前element对象添加子元素(element)
    • Element.set(key,value):为当前element的key属性设置value值
    • Element.remove(element):删除为element的节点
复制代码
#读取待修改文件
updateTree = ET.parse("test.xml")
root = updateTree.getroot()
#创建新节点并添加为root的子节点
newEle = ET.Element("NewElement")
newEle.attrib = {"name":"NewElement","age":"20"}
newEle.text = "This is a new element"
root.append(newEle)

#修改sub1的name属性
sub1 = root.find("sub1")
sub1.set("name","New Name")

#修改sub2的数据值
sub2 = root.find("sub2")
sub2.text = "New Value"

#写回原文件
updateTree.write("test.xml")
复制代码

更新完的文件为:<root><sub1 name="New Name" /><sub2>New Value</sub2><NewElement age="20" name="NewElement">This is a new element</NewElement></root>

总结

 XML的操作比较常见,当然也有很多第三方的库可以使用,所需要做的操作无非就是常用的读写xml文件、元素节点的增删改查,大家还可以在python官方文档上学习更多的操作。

https://docs.python.org/3.5/library/xml.etree.elementtree.html 

xml源文件格式[例]

[html] view plain copy
 
 print?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <framework>  
  3.     <processers>  
  4.         <processer name="AProcesser" file="lib64/A.so"  
  5.             path="/tmp">  
  6.         </processer>  
  7.         <processer name="BProcesser" file="lib64/B.so" value="fordelete">  
  8.         </processer>  
  9.         <processer name="BProcesser" file="lib64/B.so2222222"/>  
  10.           
  11.         <services>  
  12.             <service name="search" prefix="/bin/search?"  
  13.                 output_formatter="OutPutFormatter:service_inc">  
  14.                   
  15.                 <chain sequency="chain1"/>  
  16.                 <chain sequency="chain2"></chain>  
  17.             </service>  
  18.             <service name="update" prefix="/bin/update?">  
  19.                 <chain sequency="chain3" value="fordelete"/>  
  20.             </service>  
  21.         </services>  
  22.     </processers>  
  23. </framework>  


使用库:

xml.etree.ElementTree 

官方文档地址:http://docs.python.org/library/xml.etree.elementtree.html

实现思想:

使用ElementTree,先将文件读入,解析成树,之后,根据路径,可以定位到树的每个节点,再对节点进行修改,最后直接将其输出

代码附文档:

[python] view plain copy
 
 print?
  1. #!/usr/bin/python  
  2. # -*- coding=utf-8 -*-  
  3. # author : wklken@yeah.net  
  4. # date: 2012-05-25  
  5. # version: 0.1  
  6.   
  7. from xml.etree.ElementTree import ElementTree,Element  
  8.   
  9. def read_xml(in_path):  
  10.     '''''读取并解析xml文件 
  11.        in_path: xml路径 
  12.        return: ElementTree'''  
  13.     tree = ElementTree()  
  14.     tree.parse(in_path)  
  15.     return tree  
  16.   
  17. def write_xml(tree, out_path):  
  18.     '''''将xml文件写出 
  19.        tree: xml树 
  20.        out_path: 写出路径'''  
  21.     tree.write(out_path, encoding="utf-8",xml_declaration=True)  
  22.   
  23. def if_match(node, kv_map):  
  24.     '''''判断某个节点是否包含所有传入参数属性 
  25.        node: 节点 
  26.        kv_map: 属性及属性值组成的map'''  
  27.     for key in kv_map:  
  28.         if node.get(key) != kv_map.get(key):  
  29.             return False  
  30.     return True  
  31.   
  32. #---------------search -----  
  33.   
  34. def find_nodes(tree, path):  
  35.     '''''查找某个路径匹配的所有节点 
  36.        tree: xml树 
  37.        path: 节点路径'''  
  38.     return tree.findall(path)  
  39.   
  40.   
  41. def get_node_by_keyvalue(nodelist, kv_map):  
  42.     '''''根据属性及属性值定位符合的节点,返回节点 
  43.        nodelist: 节点列表 
  44.        kv_map: 匹配属性及属性值map'''  
  45.     result_nodes = []  
  46.     for node in nodelist:  
  47.         if if_match(node, kv_map):  
  48.             result_nodes.append(node)  
  49.     return result_nodes  
  50.   
  51. #---------------change -----  
  52.   
  53. def change_node_properties(nodelist, kv_map, is_delete=False):  
  54.     '''''修改/增加 /删除 节点的属性及属性值 
  55.        nodelist: 节点列表 
  56.        kv_map:属性及属性值map'''  
  57.     for node in nodelist:  
  58.         for key in kv_map:  
  59.             if is_delete:   
  60.                 if key in node.attrib:  
  61.                     del node.attrib[key]  
  62.             else:  
  63.                 node.set(key, kv_map.get(key))  
  64.               
  65. def change_node_text(nodelist, text, is_add=False, is_delete=False):  
  66.     '''''改变/增加/删除一个节点的文本 
  67.        nodelist:节点列表 
  68.        text : 更新后的文本'''  
  69.     for node in nodelist:  
  70.         if is_add:  
  71.             node.text += text  
  72.         elif is_delete:  
  73.             node.text = ""  
  74.         else:  
  75.             node.text = text  
  76.               
  77. def create_node(tag, property_map, content):  
  78.     '''''新造一个节点 
  79.        tag:节点标签 
  80.        property_map:属性及属性值map 
  81.        content: 节点闭合标签里的文本内容 
  82.        return 新节点'''  
  83.     element = Element(tag, property_map)  
  84.     element.text = content  
  85.     return element  
  86.           
  87. def add_child_node(nodelist, element):  
  88.     '''''给一个节点添加子节点 
  89.        nodelist: 节点列表 
  90.        element: 子节点'''  
  91.     for node in nodelist:  
  92.         node.append(element)  
  93.           
  94. def del_node_by_tagkeyvalue(nodelist, tag, kv_map):  
  95.     '''''同过属性及属性值定位一个节点,并删除之 
  96.        nodelist: 父节点列表 
  97.        tag:子节点标签 
  98.        kv_map: 属性及属性值列表'''  
  99.     for parent_node in nodelist:  
  100.         children = parent_node.getchildren()  
  101.         for child in children:  
  102.             if child.tag == tag and if_match(child, kv_map):  
  103.                 parent_node.remove(child)  
  104.                           
  105.   
  106.   
  107. if __name__ == "__main__":  
  108.       
  109.     #1. 读取xml文件  
  110.     tree = read_xml("./test.xml")  
  111.       
  112.     #2. 属性修改  
  113.       #A. 找到父节点  
  114.     nodes = find_nodes(tree, "processers/processer")  
  115.       #B. 通过属性准确定位子节点  
  116.     result_nodes = get_node_by_keyvalue(nodes, {"name":"BProcesser"})  
  117.       #C. 修改节点属性  
  118.     change_node_properties(result_nodes, {"age": "1"})  
  119.       #D. 删除节点属性  
  120.     change_node_properties(result_nodes, {"value":""}, True)  
  121.       
  122.     #3. 节点修改  
  123.       #A.新建节点  
  124.     a = create_node("person", {"age":"15","money":"200000"}, "this is the firest content")  
  125.       #B.插入到父节点之下  
  126.     add_child_node(result_nodes, a)  
  127.       
  128.     #4. 删除节点  
  129.        #定位父节点  
  130.     del_parent_nodes = find_nodes(tree, "processers/services/service")  
  131.        #准确定位子节点并删除之  
  132.     target_del_node = del_node_by_tagkeyvalue(del_parent_nodes, "chain", {"sequency" : "chain1"})  
  133.       
  134.     #5. 修改节点文本  
  135.        #定位节点  
  136.     text_nodes = get_node_by_keyvalue(find_nodes(tree, "processers/services/service/chain"), {"sequency":"chain3"})  
  137.     change_node_text(text_nodes, "new text")  
  138.       
  139.     #6. 输出到结果文件  
  140.     write_xml(tree, "./out.xml")  
  141.       
  142.    



通过main处理后的结果文件:

[html] view plain copy
 
 print?
    1. <?xml version='1.0' encoding='utf-8'?>  
    2. <framework>  
    3.     <processers>  
    4.         <processer file="lib64/A.so" name="AProcesser" path="/tmp">  
    5.         </processer>  
    6.         <processer age="1" file="lib64/B.so" name="BProcesser">  
    7.             <person age="15" money="200000">this is the firest content</person>  
    8.         </processer>  
    9.         <processer age="1" file="lib64/B.so2222222" name="BProcesser">  
    10.             <person age="15" money="200000">this is the firest content</person>  
    11.         </processer>  
    12.   
    13.         <services>  
    14.             <service name="search" output_formatter="OutPutFormatter:service_inc"  
    15.                 prefix="/bin/search?">  
    16.   
    17.                 <chain sequency="chain2" />  
    18.             </service>  
    19.             <service name="update" prefix="/bin/update?">  
    20.                 <chain sequency="chain3" value="fordelete">new text</chain>  
    21.             </service>  
    22.         </services>  
    23.     </processers>  
    24. </framework>  

 导入ElementTree

在使用xml.etree.ElementTree时,一般都按如下导入:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

XML是中结构化数据形式,在ET中使用ElementTree代表整个XML文档,并视其为一棵树,Element代表这个文档树中的单个节点。

ET对象具有多种方法从不同来源导入数据,如下:

复制代码
#从硬盘的xml文件读取数据

import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')    #载入数据
root = tree.getroot()    #获取根节点

#从字符串读取数据
root = ET.fromstring(country_data_as_string)
复制代码

[注意]fromstring()是直接获取string对象中的根节点,因此以上root其实是一个Element。

作为一个Element对象,本身是具有子元素,因此可以直接对Element进行迭代取值:

复制代码
>>> for child in root:
...   print child.tag, child.attrib
...
country {'name': 'Liechtenstein'}
country {'name': 'Singapore'}
country {'name': 'Panama'}

或者直接使用索引寻找子节点:
>>> root[0][1].text
'2008'
复制代码

 Element中的遍历与查询

Element.iter(tag=None):遍历该Element所有后代,也可以指定tag进行遍历寻找。

Element.findall(path):查找当前元素下tag或path能够匹配的直系节点。

Element.find(path):查找当前元素下tag或path能够匹配的首个直系节点。

Element.text: 获取当前元素的text值。

Element.get(key, default=None):获取元素指定key对应的属性值,如果没有该属性,则返回default值。

 Element对象 

复制代码
class xml.etree.ElementTree.Element(tag, attrib={}, **extra)

  tag:string,元素代表的数据种类。
  text:string,元素的内容。
  tail:string,元素的尾形。
  attrib:dictionary,元素的属性字典。
  
  #针对属性的操作
  clear():清空元素的后代、属性、text和tail也设置为None。
  get(key, default=None):获取key对应的属性值,如该属性不存在则返回default值。
  items():根据属性字典返回一个列表,列表元素为(key, value)。
  keys():返回包含所有元素属性键的列表。
  set(key, value):设置新的属性键与值。

  #针对后代的操作
  append(subelement):添加直系子元素。
  extend(subelements):增加一串元素对象作为子元素。#python2.7新特性
  find(match):寻找第一个匹配子元素,匹配对象可以为tag或path。
  findall(match):寻找所有匹配子元素,匹配对象可以为tag或path。
  findtext(match):寻找第一个匹配子元素,返回其text值。匹配对象可以为tag或path。
  insert(index, element):在指定位置插入子元素。
  iter(tag=None):生成遍历当前元素所有后代或者给定tag的后代的迭代器。#python2.7新特性
  iterfind(match):根据tag或path查找所有的后代。
  itertext():遍历所有后代并返回text值。
  remove(subelement):删除子元素。
复制代码

  

 ElementTree对象

复制代码
class xml.etree.ElementTree.ElementTree(element=None, file=None)
  element如果给定,则为新的ElementTree的根节点。

  _setroot(element):用给定的element替换当前的根节点。慎用。
  
  # 以下方法与Element类中同名方法近似,区别在于它们指定以根节点作为操作对象。
  find(match)
  findall(match)
  findtext(match, default=None)
  getroot():获取根节点.
  iter(tag=None)
  iterfind(match)
  parse(source, parser=None):装载xml对象,source可以为文件名或文件类型对象.
  write(fileencoding="us-ascii"xml_declaration=Nonedefault_namespace=None,method="xml") 
复制代码

 

 模块方法

复制代码
xml.etree.ElementTree.Comment(text=None)

创建一个特别的element,通过标准序列化使其代表了一个comment。comment可以为bytestring或unicode。

xml.etree.ElementTree.dump(elem)

生成一个element tree,通过sys.stdout输出,elem可以是元素树或单个元素。这个方法最好只用于debug。

xml.etree.ElementTree.fromstring(text)

text是一个包含XML数据的字符串,与XML()方法类似,返回一个Element实例。

xml.etree.ElementTree.fromstringlist(sequenceparser=None)

从字符串的序列对象中解析xml文档。缺省parser为XMLParser,返回Element实例。

New in version 2.7.

 

xml.etree.ElementTree.iselement(element)

检查是否是一个element对象。

xml.etree.ElementTree.iterparse(sourceevents=Noneparser=None)

将文件或包含xml数据的文件对象递增解析为element tree,并且报告进度。events是一个汇报列表,如果忽略,将只有end事件会汇报出来。

注意,iterparse()只会在看见开始标签的">"符号时才会抛出start事件,因此届时属性是已经定义了,但是text和tail属性在那时还没有定义,同样子元素也没有定义,因此他们可能不能被显示出来。如果你想要完整的元素,请查找end事件。

xml.etree.ElementTree.parse(sourceparser=None)

将一个文件或者字符串解析为element tree。

xml.etree.ElementTree.ProcessingInstruction(targettext=None)

这个方法会创建一个特别的element,该element被序列化为一个xml处理命令。

xml.etree.ElementTree.register_namespace(prefixuri)

注册命名空间前缀。这个注册是全局有效,任何已经给出的前缀或者命名空间uri的映射关系会被删除。

New in version 2.7.

 

xml.etree.ElementTree.SubElement(parenttagattrib={}**extra)

子元素工厂,创建一个Element实例并追加到已知的节点。

xml.etree.ElementTree.tostring(elementencoding="us-ascii"method="xml")

生成一个字符串来表示表示xml的element,包括所有子元素。element是Element实例,method为"xml","html","text"。返回包含了xml数据的字符串。

xml.etree.ElementTree.tostringlist(elementencoding="us-ascii"method="xml")

生成一个字符串来表示表示xml的element,包括所有子元素。element是Element实例,method为"xml","html","text"。返回包含了xml数据的字符串列表。

New in version 2.7.

xml.etree.ElementTree.XML(textparser=None)

从一个字符串常量中解析出xml片段。返回Element实例。

xml.etree.ElementTree.XMLID(textparser=None)

从字符串常量解析出xml片段,同时返回一个字典,用以映射element的id到其自身。

原文地址:https://www.cnblogs.com/JZ-Ser/p/7219428.html