在Python(以及其他编程语言)内有两种常见的方法处理XML:SAX(Simple API for XML)和DOM(Document Object Model,文档对象模型)。SAX语法分析器读取XML文件并且告知它发现的内容(文本,标签和特性)。由于它一次只村文档的一小部分,所以SAX简单,快速并能有效利用内存。DOM走的则是另外一条路:它构造一个表示整个文档的数据结构(文档树)。这样会慢些并且需要更多内存,但如果希望操作整个文档结构的话则很有用。
Python内使用DOM的信息 http://docs.python.org/2/library/xml.dom.html 。除了标准的DOM处理外,标准库还包括另外两个模块:
cml.dom.minidom(简化的DOM)和xml.dom.pulldom(SAX和DOM的结合体,减少了内存需求)。
pyRXP(https://bitbucket.org/rptlab/pyrxp) 是个快速且简单的XML语法分析器(它并不使用DOM,但是会从XML文档中建立完整的文档树)。ElementTree (http://effbot.org/zone/elementtree.htm)则更加灵活易用。
更多处理XML的Python工具请参看https://wiki.python.org/moin/PythonXml
我们使用Python内置的SAX进行解析.
SAX不用将整个文档加载到内存,基于事件驱动的API(Observer模式),用户只需要注册自己感兴趣的事件即可。在使用SAX进行解析时,有很多事件类型可用,但是这里只用到3个:元素的开始(开始标签的匹配项),元素的结束(关闭标签的匹配项)以及纯文本(字符)。要解析XML文件,可使用xml.sax模块的parse函数。这个函数负责读取文件并且生成时间——由于它要生成这3类事件,所以要调用一些事件处理程序。这些处理程序会作为内容处理程序(content handler)对象的方法来实现。需要继承 xml.sax.handler 中的 ContentHandler 类,因外它实现了所有需要的事件处理程序(只不过是没有任何效果的伪操作),可以在需要的时候覆盖这些函数。
下面是一个xml文件 website.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <website> 3 <page name="index" title="Home Page"> 4 <h1>Welcome to My Home Page</h1> 5 <p> 6 Hi, there. My name is Mr. Gumby, and this is my home page. Here 7 are some of my interests: 8 </p> 9 <ul> 10 <li> 11 <a href="interests/shouting.html">Shouting</a> 12 </li> 13 <li> 14 <a href="interests/sleeping.html">Sleeping</a> 15 </li> 16 <li> 17 <a href="interests/eating.html">Eating</a> 18 </li> 19 </ul> 20 </page> 21 <directory name="interests"> 22 <page name="shouting" title="Shouting"> 23 <h1>Mr. Gumby's Shouting Page</h1> 24 <p>...</p> 25 </page> 26 <page name="sleeping" title="Sleeping"> 27 <h1>Mr. Gumby's Sleeping Page</h1> 28 <p>...</p> 29 </page> 30 <page name="eating" title="Eating"> 31 <h1>Mr. Gumby's Eating Page</h1> 32 <p>...</p> 33 </page> 34 </directory> 35 </website>
我们需要处理上述的xml文件,根据xml的内容可知,website.xml是一个网站系统的内容文件,我们根据其意思使用Python自动生成一个简易的网站系统。
我们先用一个简单的例子演示如何使用Python SAX方式解析XML
1 import os 2 from xml.sax.handler import ContentHandler 3 from xml.sax import parse 4 5 class TestHandler(ContentHandler): 6 def startElement(self, name, attrs): 7 print name, attrs.keys() 8 9 parse('website.xml', TestHandler())
输出结果:
1 website [] 2 page [u'name', u'title'] 3 h1 [] 4 p [] 5 ul [] 6 li [] 7 a [u'href'] 8 li [] 9 a [u'href'] 10 li [] 11 a [u'href'] 12 directory [u'name'] 13 page [u'name', u'title'] 14 h1 [] 15 p [] 16 page [u'name', u'title'] 17 h1 [] 18 p [] 19 page [u'name', u'title'] 20 h1 [] 21 p []
使用SAX非常简单,如果我们对某个标签感兴趣,我们使用 if 语句进行判断然后进行相应的处理即可。
我们现在编写一个完整的Python脚本对上面的XML进行处理:
websit.xml
1 from xml.sax.handler import ContentHandler 2 from xml.sax import parse 3 import os 4 5 class Dispatcher(object): 6 7 def dispatch(self, prefix, name, attrs=None): 8 mname = prefix + name.capitalize() 9 dname = 'default' + prefix.capitalize() 10 method = getattr(self, mname, None) 11 if callable(method): 12 args = () 13 else: 14 method = getattr(self, dname, None) 15 args = name, 16 if prefix == 'start': 17 args += attrs, 18 if callable(method): 19 method(*args) 20 21 def startElement(self, name, attrs): 22 self.dispatch('start', name, attrs) 23 24 def endElement(self, name): 25 self.dispatch('end', name) 26 27 class WebsiteConstructor(Dispatcher, ContentHandler): 28 29 passthrough = False 30 31 def __init__(self, directory): 32 self.directory = [directory] 33 self.ensureDirectory() 34 35 def ensureDirectory(self): 36 path = os.path.join(*self.directory) 37 if not os.path.isdir(path): 38 os.makedirs(path) 39 40 def characters(self, content): 41 if self.passthrough: 42 self.out.write(content) 43 44 def defaultStart(self, name, attrs): 45 if self.passthrough: 46 self.out.write('<' + name) 47 for key, val in attrs.items(): 48 self.out.write(' %s="%s"' % (key, val)) 49 self.out.write('>') 50 51 def defaultEnd(self, name): 52 if self.passthrough: 53 self.out.write('</%s>' % name) 54 55 def startDirectory(self, attrs): 56 self.directory.append(attrs['name']) 57 self.ensureDirectory() 58 59 def endDirectory(self): 60 self.directory.pop() 61 62 def startPage(self, attrs): 63 filename = os.path.join(*self.directory + [attrs['name'] + '.html']) 64 self.out = open(filename, 'w') 65 self.writeHeader(attrs['title']) 66 self.passthrough = True 67 68 def endPage(self): 69 self.passthrough = False 70 self.writeFooter() 71 self.out.close() 72 73 def writeHeader(self, title): 74 self.out.write('<html> <head> <title>') 75 self.out.write(title) 76 self.out.write('</title> </head> <body> ') 77 78 def writeFooter(self): 79 self.out.write(' </body> </html> ') 80 81 XML = os.path.join('website.xml') 82 parse(XML, WebsiteConstructor('public_html'))
使用
1 python website.py
运行后我们会得到一堆HTML文件,文件内容即是xml文件定义的内容