# Python处理XML

## 通过

```python
import os
import sys
import fcntl
import xml.etree.ElementTree as ET

global example_xml
example_xml = '/home/example/example.xml

# 读取xml

lockf = None
if os.path.exists(example_xml):
    lockf = open(example_xml)
    fcntl.flock(lockf, fcntl.LOCK_EX)
    tree = ET.parse(example_xml)
    root = tree.getroot()
    logging.info("%s open and root" % example_xml)
else:
    logging.error("%s not found!" % example_xml)
    sys.exit(-1)

# 解析 name 和 memory
try:
    example_name = root.find("name").text
    logging.info("example_name is %s" % example_name)
    example['name'] = example_name
    ...
except:
    logging.exception("Can't get example_name!")
    sys.exit(-2)

# 修改添加子对象（3级） <app><color><red id='0'></red></color></app>
target = tree.find('app/color/red')
if target is None:
    print "No app/color/red tag in example.xml"
    sys.exit(-1)
target.attrib['id'] = '0'

# 完整添加子节点
e_size = "<size><high redid=\"0\" mode=\"square\" sizeset=\"%d\" /></size>" % size_set
new = ET.fromstring(e_size)
target = root.find(new.tag)
if target is not None:
    print "Tag <%s> exists in example.xml" % new.tag
    sys.exit(-3)
root.append(new)
```

> 以下是[用 ElementTree 在 Python 中解析 XML](https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/processing-xml-in-python-with-element-tree.html)摘要，便于后续自己查询：使用ET处理XML确实非常方便直观，推荐使用。不过，我在实践编程时在项目上使用这些方法并没有完全实现我的目标，也许我使用方法还有问题。实际开发过程中，我使用了同事给我的参考代码进行整合，见上文。

xml.etree.ElementTree (以下简称 ET)。它提供了轻量级的 Python 式的 API ，它由一个 C 实现来提供。

ElementTree 生来就是为了处理 XML ，它在 Python 标准库中有两种实现。一种是纯 Python 实现例如 xml.etree.ElementTree ，另外一种是速度快一点的 xml.etree.cElementTree 。

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

> 从 Python 3.3 开始，ElementTree 模块会自动寻找可用的 C 库来加快速度

## 将 XML 解析为树的形式

XML 是一种分级的数据形式，所以最自然的表示方法是将它表示为一棵树。ET 有两个对象来实现这个目的 － ElementTree 将整个 XML 解析为一棵树， Element 将单个结点解析为树。

XML 文件例子

```markup
<?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 ：

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

抓根结点元素：

```python
tree.getroot()
```

root 是一个 Element 元素

```python
root = tree.getroot()
root.tag, root.attrib
```

根元素没有任何状态，可以找到自己的子结点：

```python
for child_of_root in root:
    print child_of_root.tag, child_of_root.attrib
```

## 找到元素

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

```python
for elem in tree.iter():
    print elem.tag, elem.attrib
```

`iter` 方法接受一个标签名字，然后只遍历那些有指定标签的元素：

```python
for elem in tree.iter(tag='branch'):
    print elem.tag, elem.attrib
```

Element 有一些关于寻找的方法可以接受 [XPath](http://effbot.org/zone/element-xpath.htm) 作为参数。 find 返回第一个匹配的子元素， findall 以列表的形式返回所有匹配的子元素， iterfind 为所有匹配项提供迭代器。这些方法在 ElementTree 里面也有。

```python
for elem in tree.iterfind('branch/sub-branch'):
    print elem.tag, elem.attrib
```

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

```python
for elem in tree.iterfind('branch[@name="release01"]'):
    print elem.tag, elem.attrib
```

## 建立 XML 文档

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

```python
root = tree.getroot()
del root[2]
root[0].set('foo', 'bar')
for subelem in root:
    print subelem.tag, subelem.attrib
```

* 写回文件

```python
import sys
tree.write(sys.stdout)
```

* 建立一个全新的元素，ET 模块提供了 SubElement 函数来简化过程：

```python
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)
```

## 使用 iterparse 来处理 XML 流

XML 文档通常比较大, ET 提供一个特殊的工具，用来处理大型文档，并且解决了内存问题，这个工具叫 iterparse 。

案例XML文件如下：

```markup
<?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 的一个标准的写法：

```python
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 的来源：

```python
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 仍然建立一棵树，只不过不需要全部加载进内存，这样做可以有效的利用内存空间。

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

## 参考

* [用 ElementTree 在 Python 中解析 XML](https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/processing-xml-in-python-with-element-tree.html)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://huataihuang.gitbook.io/cloud-atlas-draft/develop/python/startup/xml.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
