Python 解析 XML 数据的正确姿势( 四 )

  1. >>> import sys
  2. >>> tree.write(sys.stdout)
  3. <doc>
  4. <branch foo="bar" hash="1cdf045c" name="codingpy.com">
  5. text,source
  6. </branch>
  7. <branch hash="f200013e" name="release01">
  8. <sub-branch name="subrelease01">
  9. xml,sgml
  10. </sub-branch>
  11. </branch>
  12. </doc>
请注意,文档中元素的属性顺序与原文档不同 。这是因为ET是以字典的形式保存属性的,而字典是一个无序的数据结构 。当然,XML也不关注属性的顺序 。
从头构建一个完整的文档也很容易 。ET模块提供了一个 SubElement工厂函数,让创建元素的过程变得很简单:
 
  1. >>> a = ET.Element('elem')
  2. >>> c = ET.SubElement(a, 'child1')
  3. >>> c.text = "some text"
  4. >>> d = ET.SubElement(a, 'child2')
  5. >>> b = ET.Element('elem_b')
  6. >>> root = ET.Element('root')
  7. >>> root.extend((a, b))
  8. >>> tree = ET.ElementTree(root)
  9. >>> tree.write(sys.stdout)
  10. <root><elem><child1>some text</child1><child2 /></elem><elem_b /></root>
 
利用 iterparse解析XML流XML文档通常都会比较大,如何直接将文档读入内存的话,那么进行解析时就会出现问题 。这也就是为什么不建议使用DOM,而是SAX API的理由之一 。
我们上面谈到,ET可以将XML文档加载为保存在内存里的树(in-memory tree),然后再进行处理 。但是在解析大文件时,这应该也会出现和DOM一样的内存消耗大的问题吧?没错,的确有这个问题 。为了解决这个问题,ET提供了一个类似SAX的特殊工具—— iterparse,可以循序地解析XML 。
接下来,笔者为大家展示如何使用 iterparse,并与标准的树解析方式进行对比 。我们使用一个自动生成的XML文档,下面是该文档的开头部分:
 
  1. <?xml version="1.0" standalone="yes"?>
  2. <site>
  3. <regions>
  4. <africa>
  5. <item id="item0">
  6. <location>United States</location> <!-- Counting locations -->
  7. <quantity>1</quantity>
  8. <name>duteous nine eighteen </name>
  9. <payment>Creditcard</payment>
  10. <description>
  11. <parlist>
  12. [...]
我们来统计一下文档中出现了多少个文本值为Zimbabwe的location元素 。下面是使用 ET.parse的标准方法:
 
  1. tree = ET.parse(sys.argv[2])

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

  6. print count
上面的代码会将全部元素载入内存,逐一解析 。当解析一个约100MB的XML文档时,运行上面脚本的Python进程的内存使用峰值为约560MB,总运行时间问2.9秒 。
请注意,我们其实不需要讲整个树加载到内存里 。只要检测出文本为相应值得location元素即可 。其他数据都可以废弃 。这时,我们就可以用上iterparse方法了:
 
  1. count = 0
  2. for event, elem in ET.iterparse(sys.argv[2]):
  3. if event == 'end':
  4. if elem.tag == 'location' and elem.text == 'Zimbabwe':
  5. count += 1
  6. elem.clear # 将元素废弃

  7. print count
上面的for循环会遍历iterparse事件,首先检查事件是否为 


推荐阅读