geekdoc-python-zh/docs/pythonlibrary/python-parsing-xml-with-min...

258 lines
12 KiB
Markdown
Raw Permalink Normal View History

2024-10-20 12:24:46 +08:00
# Python:用 minidom 解析 XML
> 原文:<https://www.blog.pythonlibrary.org/2010/11/12/python-parsing-xml-with-minidom/>
如果你是一个长期读者,你可能记得我在 2006 年开始编程 Python。在一年左右的时间里我的雇主决定从 Microsoft Exchange 迁移到开源的 Zimbra 客户端。Zimbra 是一个不错的客户端,但它缺少一种好的方式来提醒用户他们有一个约会,所以我必须创建一种方法来查询 Zimbra 的信息并显示一个对话框。但是,所有这些晦涩难懂的东西与 XML 有什么关系呢?嗯,我认为使用 XML 是跟踪哪些约会被添加、删除、暂停或其他什么的好方法。结果证明我错了,但这不是这个故事的重点。
在本文中,我们将看到我第一次尝试用 Python 解析 XML。如果您对这个主题做一点研究您很快就会发现 Python 在它的 **xml** 模块中内置了一个 XML 解析器。我最终使用了那个模块的 **minidom** 子组件...至少一开始是这样。最终我改用 lxml它使用 ElementTree但这超出了本文的范围。让我们快速看一下我想到的一些难看的 XML:
```py
<appointment><begin>1181251680</begin>
<uid>040000008200E000</uid>
<alarmtime>1181572063</alarmtime>
<state><location><duration>1800</duration>
<subject>Bring pizza home</subject></location></state></appointment>
```
现在我们知道我需要解析什么了。让我们看看在 Python 中使用 minidom 解析类似内容的典型方式。
```py
import xml.dom.minidom
import urllib2
class ApptParser(object):
def __init__(self, url, flag='url'):
self.list = []
self.appt_list = []
self.flag = flag
self.rem_value = 0
xml = self.getXml(url)
print "xml"
print xml
self.handleXml(xml)
def getXml(self, url):
try:
print url
f = urllib2.urlopen(url)
except:
f = url
#print f
doc = xml.dom.minidom.parse(f)
node = doc.documentElement
if node.nodeType == xml.dom.Node.ELEMENT_NODE:
print 'Element name: %s' % node.nodeName
for (name, value) in node.attributes.items():
#print ' Attr -- Name: %s Value: %s' % (name, value)
if name == 'reminder':
self.rem_value = value
return node
def handleXml(self, xml):
rem = xml.getElementsByTagName('zAppointments')
appointments = xml.getElementsByTagName("appointment")
self.handleAppts(appointments)
def getElement(self, element):
return self.getText(element.childNodes)
def handleAppts(self, appts):
for appt in appts:
self.handleAppt(appt)
self.list = []
def handleAppt(self, appt):
begin = self.getElement(appt.getElementsByTagName("begin")[0])
duration = self.getElement(appt.getElementsByTagName("duration")[0])
subject = self.getElement(appt.getElementsByTagName("subject")[0])
location = self.getElement(appt.getElementsByTagName("location")[0])
uid = self.getElement(appt.getElementsByTagName("uid")[0])
self.list.append(begin)
self.list.append(duration)
self.list.append(subject)
self.list.append(location)
self.list.append(uid)
if self.flag == 'file':
try:
state = self.getElement(appt.getElementsByTagName("state")[0])
self.list.append(state)
alarm = self.getElement(appt.getElementsByTagName("alarmTime")[0])
self.list.append(alarm)
except Exception, e:
print e
self.appt_list.append(self.list)
def getText(self, nodelist):
rc = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
return rc
```
如果我没记错的话,这段代码是基于 Python 文档中的一个例子(或者是深入 Python 中的一个章节)。我还是不喜欢这个代码。您在 **ApptParser** 类中看到的 **url** 参数可以是 url也可以是文件。我有一个来自 Zimbra 的 XML 提要,我会定期检查它的变化,并将其与我下载的 XML 的最后一个副本进行比较。如果有新的东西,我会把修改添加到下载的副本中。无论如何,让我们稍微解开这个代码。
**getXml** 中,我们使用一个异常处理程序来尝试打开 url。如果它碰巧引发了一个错误那么我们假设这个 url 实际上是一个文件路径。接下来,我们使用 minidom 的**解析**方法来解析 XML。然后我们从 XML 中取出一个节点。我们将忽略条件句,因为它对这个讨论不重要(它与我的程序有关)。最后,我们返回节点对象。
从技术上讲,节点是 XML我们将它传递给 **handleXml** 。为了获取 XML 中所有的**约会**实例,我们这样做: *xml.getElementsByTagName("约会")*。然后,我们将该信息传递给**处理设备**方法。是的,到处都在传递各种价值观。试图跟踪它并在以后调试它让我发疯。总之,*handle appt*方法所做的就是循环每个约会,并调用 **handleAppt** 方法从中提取一些附加信息,将数据添加到一个列表,并将该列表添加到另一个列表。我的想法是以一个包含所有与我的约会相关的数据的列表结束。
你会注意到 **handleAppt** 方法调用了 **getElement** 方法,后者调用了 **getText** 方法。我不知道原作者为什么要这样做。我会直接调用 **getText** 方法,跳过 getElement 方法。亲爱的读者,我想这对你是一种锻炼。
现在您已经了解了使用 minidom 进行解析的基础知识。我个人不喜欢这种方法,所以我决定尝试用 minidom 提出一种更简洁的解析 XML 的方法。
## 让 minidom 更容易关注
我不会说我的代码有什么好的,但是我会说我认为我想出了一些更容易理解的东西。我肯定有些人会认为代码不够灵活,但是没关系。下面是我们将要解析的一个新的 XML 示例(在 [MSDN](http://msdn.microsoft.com/en-us/library/ms762271%28VS.85%29.aspx) 上找到的):
```py
<book id="bk101"><author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description></book>
<book id="bk102"><author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description></book>
<book id="bk103"><author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description></book>
<book id="bk104"><author>Corets, Eva</author>
<title>Oberon's Legacy</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-03-10</publish_date>
<description>In post-apocalypse England, the mysterious
agent known only as Oberon helps to create a new life
for the inhabitants of London. Sequel to Maeve
Ascendant.</description></book>
<book id="bk105"><author>Corets, Eva</author>
<title>The Sundered Grail</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-09-10</publish_date>
<description>The two daughters of Maeve, half-sisters,
battle one another for control of England. Sequel to
Oberon's Legacy.</description></book>
<book id="bk106"><author>Randall, Cynthia</author>
<title>Lover Birds</title>
<genre>Romance</genre>
<price>4.95</price>
<publish_date>2000-09-02</publish_date>
<description>When Carla meets Paul at an ornithology
conference, tempers fly as feathers get ruffled.</description></book>
<book id="bk107"><author>Thurman, Paula</author>
<title>Splish Splash</title>
<genre>Romance</genre>
<price>4.95</price>
<publish_date>2000-11-02</publish_date>
<description>A deep sea diver finds true love twenty
thousand leagues beneath the sea.</description></book>
<book id="bk108"><author>Knorr, Stefan</author>
<title>Creepy Crawlies</title>
<genre>Horror</genre>
<price>4.95</price>
<publish_date>2000-12-06</publish_date>
<description>An anthology of horror stories about roaches,
centipedes, scorpions and other insects.</description></book>
<book id="bk109"><author>Kress, Peter</author>
<title>Paradox Lost</title>
<genre>Science Fiction</genre>
<price>6.95</price>
<publish_date>2000-11-02</publish_date>
<description>After an inadvertant trip through a Heisenberg
Uncertainty Device, James Salway discovers the problems
of being quantum.</description></book>
<book id="bk110"><author>O'Brien, Tim</author>
<title>Microsoft .NET: The Programming Bible</title>
<genre>Computer</genre>
<price>36.95</price>
<publish_date>2000-12-09</publish_date>
<description>Microsoft's .NET initiative is explored in
detail in this deep programmer's reference.</description></book>
<book id="bk111"><author>O'Brien, Tim</author>
<title>MSXML3: A Comprehensive Guide</title>
<genre>Computer</genre>
<price>36.95</price>
<publish_date>2000-12-01</publish_date>
<description>The Microsoft MSXML3 parser is covered in
detail, with attention to XML DOM interfaces, XSLT processing,
SAX and more.</description></book>
<book id="bk112"><author>Galos, Mike</author>
<title>Visual Studio 7: A Comprehensive Guide</title>
<genre>Computer</genre>
<price>49.95</price>
<publish_date>2001-04-16</publish_date>
<description>Microsoft Visual Studio 7 is explored in depth,
looking at how Visual Basic, Visual C++, C#, and ASP+ are
integrated into a comprehensive development
environment.</description></book>
```
对于这个例子,我们只需要解析 XML提取书名并打印到 stdout。你准备好了吗开始了。
```py
import xml.dom.minidom as minidom
#----------------------------------------------------------------------
def getTitles(xml):
"""
Print out all titles found in xml
"""
doc = minidom.parse(xml)
node = doc.documentElement
books = doc.getElementsByTagName("book")
titles = []
for book in books:
titleObj = book.getElementsByTagName("title")[0]
titles.append(titleObj)
for title in titles:
nodes = title.childNodes
for node in nodes:
if node.nodeType == node.TEXT_NODE:
print node.data
if __name__ == "__main__":
document = 'example.xml'
getTitles(document)
```
这段代码只是一个接受一个参数(XML 文件)的简短函数。我们导入 minidom 模块,并给它相同的名称,以便更容易引用。然后我们解析 XML。函数中的前两行与前一个例子非常相似。我们使用 **getElementsByTagName** 获取我们想要的 XML 部分,然后迭代结果并从中提取书名。这实际上提取了标题对象,所以我们也需要对其进行迭代并提取纯文本,这就是第二个嵌套的 **for** 循环的目的。
就是这样。再也没有了。
## 包扎
好吧,我希望这篇漫无边际的文章能够教会您一些关于使用 Python 内置的 XML 解析器解析 XML 的知识。在以后的文章中,我们将会看到更多的 XML 解析。如果您有自己喜欢的方法或模块,请随意给我指出来,我会看一看。
## 附加阅读
* Python minidom [官方文档](http://docs.python.org/library/xml.dom.minidom.html)
* Python 和 XML [维基页面](http://wiki.python.org/moin/PythonXml)
* Python 的其他内置 XML 解析器: [ElementTree](http://docs.python.org/library/xml.etree.elementtree.html) 、 [sax、](http://docs.python.org/library/xml.sax.html)、 [expat](http://docs.python.org/library/pyexpat.html) 和 [dom](http://docs.python.org/library/xml.dom.html)