07.XML处理之SAX编程
Python标准库xml模块提供4个功能模块:
sax -- 基于事件的XML文档处理;
dom -- DOM接口实现;
etree -- 文档树
parsers -- XML解析器的Python包装器
一、sax功能模块
import xml.sax
# help(xml.sax)
1. sax API结构
sax提供四个模块来处理xml:
xml.sax.xmlreader — xml的解析接口规范,抽象的,需要继承实现才能使用。
xml.sax.handler — 回调处理器,如果与xmlreader绑定,当满足xml条件就会被调用,用来处理具体的xml内容。
xml.sax.saxutils — 一些数据处理工具,比如实体转换什么的。
xml.parsers.expat — xmlreader的具体实现,可以直接使用。也可以使用parse函数直接解析,或者使用make_parser返回解析器,或者直接构造ExpatParser解析器使用。
下面对核心模块xmlreader与handler进一步说明。
1.1. xmlreader API结构
提供如下几个类实现数据分析:
|- AttributesImpl
|- AttributesNSImpl
|- InputSource
|- Locator
|- XMLReader
|- IncrementalParser
1.2. handler API结构
提供4个Handler来处理xml内容。
|- ContentHandler
|- DTDHandler
|- EntityResolver
|- ErrorHandler
2. sax解析器的三种使用方式
2.1. sax编程模式
# coding = utf-8
import xml.sax.handler
class MyHandler(xml.sax.handler.ContentHandler, xml.sax.handler.DTDHandler):
def setDocumentLocator(self, locator):
print('setDocumentLocator')
def startDocument(self):
print('startDocument')
def endDocument(self):
print('endDocument')
def startPrefixMapping(self, prefix, uri):
print('startPrefixMapping')
def endPrefixMapping(self, prefix):
print('endPrefixMapping')
def startElement(self, name, attrs):
print('startElement')
def endElement(self, name):
print('endElement')
def startElementNS(self, name, qname, attrs):
print('startElementNS')
def endElementNS(self, name, qname):
print('endElementNS')
def characters(self, content):
print('characters')
def ignorableWhitespace(self, whitespace):
print('ignorableWhitespace')
def processingInstruction(self, target, data):
print('processingInstruction')
def skippedEntity(self, name):
print('skippedEntity')
def notationDecl(self, name, publicId, systemId):
print('notationDecl')
def unparsedEntityDecl(self, name, publicId, systemId, ndata):
print('unparsedEntityDecl')
class MyError(xml.sax.ErrorHandler):
def error(self, exception):
print('error')
return super().error(exception)
def fatalError(self, exception):
print('fatalError')
return super().fatalError(exception)
def warning(self, exception):
print('warning')
super().warning(exception)
xml.sax.parse(
source='codes/note.xml',
handler=MyHandler(),
errorHandler=MyError())
setDocumentLocator
startDocument
startElement
characters
characters
startElement
characters
endElement
characters
characters
startElement
characters
endElement
characters
characters
startElement
characters
endElement
characters
characters
startElement
characters
endElement
characters
endElement
endDocument
2.3. 构造parser
注意:不能直接构造XMLReader,而是使用工厂模式函数:
xml.sax.make_parser()来产生parser,要么直接调用parse函数解析处理xml。
XMLReader是xml解析的接口规范,其实现就是:xml.sax.expatreader.ExpatParser
# coding = utf-8
import xml.sax.handler
import xml.sax.xmlreader
class MyHandler(xml.sax.handler.ContentHandler,xml.sax.handler.DTDHandler):
def setDocumentLocator(self, locator):
print('setDocumentLocator')
class MyError(xml.sax.ErrorHandler):
def error(self, exception):
print('error')
return super().error(exception)
'''
xml.sax.parse(
source='books.xml',
handler=MyHandler(),
errorHandler=MyError())
'''
handler = MyHandler()
error = MyError()
parser = xml.sax.make_parser()
print(parser)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/note.xml')
<xml.sax.expatreader.ExpatParser object at 0x103f92160>
setDocumentLocator
2.4. ExpatParser的使用
ExpatParser实现的接口类:
|- class ExpatParser(xml.sax.xmlreader.IncrementalParser, xml.sax.xmlreader.Locator)
下面是使用例子:
# coding = utf-8
import xml.sax.handler
import xml.sax.xmlreader
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler,xml.sax.handler.DTDHandler):
def setDocumentLocator(self, locator):
print('setDocumentLocator')
class MyError(xml.sax.ErrorHandler):
def error(self, exception):
print('error')
return super().error(exception)
'''
xml.sax.parse(
source='books.xml',
handler=MyHandler(),
errorHandler=MyError())
'''
handler = MyHandler()
error = MyError()
# parser = xml.sax.make_parser()
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=True)
print(parser)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/note.xml')
<xml.sax.expatreader.ExpatParser object at 0x103f9cbe0>
setDocumentLocator
3. handler的使用
对DTDHandler这里不说明,
3.1. Locator的使用
Locator是文档定位,其实现类是ExpatLocator类。
|- xml.sax.xmlreader.Locator(builtins.object)
|- xml.sax.expatreader.ExpatLocator
该类使用是个函数提供四个信息:
| - getColumnNumber(self) xml当前列数
| - getLineNumber(self) xml当前行数
| - getPublicId(self) xml的public ID:在Python中通常是None
| - getSystemId(self) xml的system ID:通常就是xml文件
# coding = utf-8
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler):
def setDocumentLocator(self, locator):
print(f'system ID:{locator.getSystemId()}')
print(f'public ID:{locator.getPublicId()}')
print('行列位置(%d,%d)' % (locator.getLineNumber(),locator.getColumnNumber()))
handler = MyHandler()
error = xml.sax.ErrorHandler()
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=True)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/web.xml')
system ID:codes/web.xml
public ID:None
行列位置(1,0)
3.2. startDocument与endDocument的文档解析事件函数
在文档解析开始的时候触发解析事件函数:startDocument,在文档解析结束的时候触发解析事件函数:endDocument。
# coding = utf-8
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler):
def startDocument(self):
print('开始文档解析')
def endDocument(self):
print('文档解析结束')
handler = MyHandler()
error = xml.sax.ErrorHandler()
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=True)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/books.xml')
开始文档解析
文档解析结束
3.3. startElement与endElement元素解析事件函数
startElement函数在每个元素开始前触发。
endElement函数在每个元素结束后触发。
函数的原型如下:
|- def startElement(self, name, attrs):
|- name:类型是字符串str,表示元素名。
|- attrs:类型是xml.sax.xmlreader.AttributesImpl,表示元素的所有属性。
|- def endElement(self, name):
其中attrs是属性列表,本质是一个字典,还有一些其他成员,可以使用帮助方式查看。
# coding = utf-8
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler):
def startElement(self, name, attrs):
print('元素解析开始,元素名:', name)
# attrs是属性列表
print(type(attrs), type(name))
# 属性列表
print('属性数:%d' % attrs.getLength())
for item in attrs.items():
print('属性名:%s,属性值:%s' % item)
def endElement(self, name):
print('元素解析结束,元素名:', name)
handler = MyHandler()
error = xml.sax.ErrorHandler()
# namespaceHandling 对具有命名空间的元素处理,没有命名空间设置围为0。
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=0)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/books.xml')
元素解析开始,元素名: books
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析开始,元素名: book
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:1
属性名:category,属性值:Python
元素解析开始,元素名: title
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:1
属性名:lang,属性值:zh
元素解析结束,元素名: title
元素解析开始,元素名: author
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: author
元素解析开始,元素名: year
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: year
元素解析开始,元素名: price
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: price
元素解析开始,元素名: publisher
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: publisher
元素解析结束,元素名: book
元素解析开始,元素名: book
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:1
属性名:category,属性值:系统运维
元素解析开始,元素名: title
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:1
属性名:lang,属性值:zh
元素解析结束,元素名: title
元素解析开始,元素名: author
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: author
元素解析开始,元素名: year
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: year
元素解析开始,元素名: price
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: price
元素解析开始,元素名: publisher
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: publisher
元素解析结束,元素名: book
元素解析开始,元素名: book
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:1
属性名:category,属性值:区块链
元素解析开始,元素名: title
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:1
属性名:lang,属性值:en
元素解析结束,元素名: title
元素解析开始,元素名: author
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: author
元素解析开始,元素名: year
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: year
元素解析开始,元素名: price
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: price
元素解析开始,元素名: publisher
<class 'xml.sax.xmlreader.AttributesImpl'> <class 'str'>
属性数:0
元素解析结束,元素名: publisher
元素解析结束,元素名: book
元素解析结束,元素名: books
3.4. characters文本解析事件函数
函数原型是:characters(self, content)
参数:content是文本内容。
# coding = utf-8
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler):
def characters(self, content):
# 为了看见所有的字符,下面输出采用子节序列。
# 下面的文本包含所有元素结束的换行字符。
print(content.encode('utf-8'))
handler = MyHandler()
error = xml.sax.ErrorHandler()
# namespaceHandling 对具有命名空间的元素处理,没有命名空间设置围为0。
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=0)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/note.xml')
b'\n'
b' '
b'Tove'
b'\n'
b' '
b'Jani'
b'\n'
b' '
b'Reminder'
b'\n'
b' '
b"Don't forget me this weekend!"
b'\n'
3.5. startElementNS与endElementNS带命名空间名的元素解析事件函数
startElementNS(self, name, qname, attrs)
|- qname:命名空间,qname是qualified name 的简写,比如:<song:id>。
|- name:与startElement不同,这里是一个元组,格式如下:(uri, localname)
# coding = utf-8
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler):
def startElementNS(self, name, qname, attrs):
print(name, qname)
def endElementNS(self, name, qname):
print('结束:', name)
handler = MyHandler()
error = xml.sax.ErrorHandler()
# namespaceHandling 对具有命名空间的元素处理,没有命名空间设置围为0。
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=True)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/song.xml')
('http://www.example.org/athena', 'body') None
('http://www.example.org/athena', 'id') None
结束: ('http://www.example.org/athena', 'id')
('http://www.example.org/athena', 'name') None
结束: ('http://www.example.org/athena', 'name')
('http://www.example.org/athena', 'birthday') None
结束: ('http://www.example.org/athena', 'birthday')
结束: ('http://www.example.org/athena', 'body')
3.6. startPrefixMapping与endPrefixMapping解析事件函数
命名空间前缀映射解析事件。
startPrefixMapping(self, prefix, uri):
|- prefix :前缀
|- uri:uri
# coding = utf-8
import xml.sax.expatreader
class MyHandler(xml.sax.handler.ContentHandler):
def startPrefixMapping(self, prefix, uri):
print(prefix, uri)
def endPrefixMapping(self, prefix):
print(prefix)
handler = MyHandler()
error = xml.sax.ErrorHandler()
# namespaceHandling 对具有命名空间的元素处理,没有命名空间设置围为0。
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=True)
parser.setContentHandler(handler)
parser.setErrorHandler(error)
parser.setDTDHandler(handler)
parser.parse('codes/song.xml')
song http://www.example.org/athena
xsi http://www.w3.org/2001/XMLSchema-instance
xsi
song
3.7. 其他
还有一些其他事件函数,这里不详细解释。比如:
processingInstruction(self, target, data)函数就是处理'<?xml version="1.0" encoding="UTF-8"?>'。但实际不触发该函数。
注意:
SAX解析处理XML比较麻烦,我们都不直接使用,这里只是说明xml的处理底层细节。
总结:
sax模块结构分成两个标准接口定义:handler与xmlreader,一个实现模块expatreader,一个工具模块saxutils。