9.3. XML 解析
Description: Python from novice to pro
Keywords: Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free

# 深入 Python :Dive Into Python 中文版 Python 从新手到专家 [Dip_5.4b_CPyUG_Release] | Find:

## 9.3. XML 解析

正如我说的，实际解析一个 XML 文档是非常简单的：只要一行代码。从这里出发到哪儿去就是你自己的事了。

### 例 9.8. 载入一个 XML 文档 (这次是真的)

    >>> from xml.dom import minidom
    >>> xmldoc = minidom.parse('~/diveintopython/common/py/kgp/binary.xml')
    >>> xmldoc
    <xml.dom.minidom.Document instance at 010BE87C>
    >>> print xmldoc.toxml()
    <?xml version="1.0" ?>
    <grammar>
    <ref id="bit">
      <p>0</p>
      <p>1</p>
    </ref>
    <ref id="byte">
      <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
    <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
    </ref>
    </grammar>

| 正如在上一节看到的，该语句从 `xml.dom` 包中导入 `minidom` 模块。
| 这就是进行所有工作的一行代码：`minidom.parse` 接收一个参数并返回 XML 文档解析后的表示形式。这个参数可以是很多东西；在本例中，它只是我本地磁盘上一个 XML 文档的文件名。(你需要将路径改为指向下载的例子所在的目录。) 但是你也可以传入一个文件对象，或甚至是一个类文件对象。这样你就可以在本章后面好好利用这一灵活性了。
| 从 `minidom.parse` 返回的对象是一个 `Document` 对象，它是 `Node` 类的一个子对象。这个 `Document` 对象是联锁的 Python 对象的一个复杂树状结构的根层次，这些 Python 对象完整表示了传给 `minidom.parse` 的 XML 文档。
| `toxml` 是 `Node` 类的一个方法 (因此可以在从 `minidom.parse` 中得到的 `Document` 对象上使用)。`toxml` 打印出了 `Node` 表示的 XML。对于 `Document` 节点，这样就会打印出整个 XML 文档。

现在内存中已经有了一个 XML 文档了，你可以开始遍历它了。

### 例 9.9. 获取子节点

    >>> xmldoc.childNodes
    [<DOM Element: grammar at 17538908>]
    >>> xmldoc.childNodes[0]
    <DOM Element: grammar at 17538908>
    >>> xmldoc.firstChild
    <DOM Element: grammar at 17538908>

| 每个 `Node` 都有一个 `childNodes` 属性，它是一个 `Node` 对象的列表。一个 `Document` 只有一个子节点，即 XML 文档的根元素 (在本例中，是 `grammar` 元素)。
| 为了得到第一个 (在本例中，只有一个) 子节点，只要使用正规的列表语法。回想一下，其实这里没有发生什么特别的；这只是一个由正规 Python 对象构成的正规 Python 列表。
| 鉴于获取某个节点的第一个子节点是有用而且常见的行为，所以 `Node` 类有一个 `firstChild` 属性，它和`childNodes[0]`具有相同的语义。(还有一个 `lastChild` 属性，它和`childNodes[-1]`具有相同的语义。)

### 例 9.10. `toxml` 用于任何节点

    >>> grammarNode = xmldoc.firstChild
    >>> print grammarNode.toxml()
    <grammar>
    <ref id="bit">
      <p>0</p>
      <p>1</p>
    </ref>
    <ref id="byte">
      <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
    <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
    </ref>
    </grammar>

| 由于 `toxml` 方法是定义在 `Node` 类中的，所以对任何 XML 节点都是可用的，不仅仅是 `Document` 元素。

### 例 9.11. 子节点可以是文本

    >>> grammarNode.childNodes
    [<DOM Text node "\n">, <DOM Element: ref at 17533332>, \
    <DOM Text node "\n">, <DOM Element: ref at 17549660>, <DOM Text node "\n">]
    >>> print grammarNode.firstChild.toxml()

    >>> print grammarNode.childNodes[1].toxml()
    <ref id="bit">
      <p>0</p>
      <p>1</p>
    </ref>
    >>> print grammarNode.childNodes[3].toxml()
    <ref id="byte">
      <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
    <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
    </ref>
    >>> print grammarNode.lastChild.toxml()

| 查看 `binary.xml` 中的 XML ，你可能会认为 `grammar` 只有两个子节点，即两个 `ref` 元素。但是你忘记了一些东西：硬回车！在`'<grammar>'`之后，第一个`'<ref>'`之前是一个硬回车，并且这个文本算作 `grammar` 元素的一个子节点。类似地，在每个`'</ref>'`之后都有一个硬回车；它们都被当作子节点。所以`grammar.childNodes`实际上是一个有5个对象的列表：3个 `Text` 对象和两个 `Element` 对象。
| 第一个子节点是一个 `Text` 对象，它表示在`'<grammar>'`标记之后、第一个`'<ref>'`标记之后的硬回车。
| 第二个子节点是一个 `Element` 对象，表示了第一个 `ref` 元素。
| 第四个子节点是一个 `Element` 对象，表示了第二个 `ref` 元素。
| 最后一个子节点是一个 `Text` 对象，表示了在`'</ref>'`结束标记之后、`'</grammar>'` 结束标记之前的硬回车。

### 例 9.12. 把文本挖出来

    >>> grammarNode
    <DOM Element: grammar at 19167148>
    >>> refNode = grammarNode.childNodes[1]
    >>> refNode
    <DOM Element: ref at 17987740>
    >>> refNode.childNodes
    [<DOM Text node "\n">, <DOM Text node "  ">, <DOM Element: p at 19315844>, \
    <DOM Text node "\n">, <DOM Text node "  ">, \
    <DOM Element: p at 19462036>, <DOM Text node "\n">]
    >>> pNode = refNode.childNodes[2]
    >>> pNode
    <DOM Element: p at 19315844>
    >>> print pNode.toxml()
    <p>0</p>
    >>> pNode.firstChild
    <DOM Text node "0">
    >>> pNode.firstChild.data
    u'0'

| 正如你在前面的例子中看到的，第一个 `ref` 元素是 `grammarNode.childNodes[1]`，因为 childNodes[0] 是一个代表硬回车的 `Text` 节点。
| `ref` 元素有它自己的子节点集合，一个表示硬回车，另一个表示空格，一个表示 `p` 元素，诸如此类。
| 你甚至可以在这里使用 `toxml` 方法，尽管它深深嵌套在文档中。
| `p` 元素只有一个子节点 (在这个例子中无法看出，但是如果你不信，可以看看`pNode.childNodes`)，而且它是表示单字符`'0'`的一个 `Text` 节点。
| `Text` 节点的 `.data` 属性可以向你提供文本节点真正代表的字符串。但是字符串前面的`'u'`是什么意思呢？答案将自己专门有一部分来论述。