本文链接: https://blog.csdn.net/xietansheng/article/details/115559132
常用的 XML 解析有 SAX 和 DOM 两种解析方式(还有 PULL 方式)。
SAX 解析以事件驱动方式一边读取一边解析 XML 文档,DOM 则将整个 XML 文档读取到内存中形成一颗 DOM 树后再进行操作。
Python 标准库中包含了 XML 的 SAX 和 DOM 解析。
Python Docs:
Python 中的 SAX 解析,主要涉及到一个类xml.sax.handler.ContentHandler
,使用时需自定义一个类继承 ContentHandler 并重写其方法用于接收 XML 文档解析过程的事件回调。
Python Docs: contenthandler-objects
xml.sax.handler.ContentHandler
类中的方法:
ContentHandler.setDocumentLocator(locator)
ContentHandler.startDocument()
ContentHandler.endDocument()
ContentHandler.startPrefixMapping(prefix, uri)
ContentHandler.endPrefixMapping(prefix)
ContentHandler.startElement(name, attrs)
ContentHandler.endElement(name)
ContentHandler.startElementNS(name, qname, attrs)
ContentHandler.endElementNS(name, qname)
ContentHandler.characters(content)
ContentHandler.ignorableWhitespace(whitespace)
ContentHandler.processingInstruction(target, data)
ContentHandler.skippedEntity(name)
解析文件,文件名: books.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
<book id="123">
<name>Python</name>
<price>50.00</price>
</book>
<book id="456">
<name>Java</name>
<price>60.00</price>
</book>
<book id="789">
<name><![CDATA[C/C++]]></name>
<price>30.00</price>
</book>
</root>
SAX 解析 XML 代码示例:
import xml.sax
class Book:
"""
Book 类, 用于保存 xml 中 <book/> 元素节点的数据
"""
id: int = 0
name: str = None
price: float = 0.0
def __str__(self):
return "Book: id=%d, name=%s, price=%.2f" % (self.id, self.name, self.price)
class BooksContentHandler(xml.sax.handler.ContentHandler):
"""
用于解析 Books XML 的 Handler
"""
books: list = None # 用于保存所有的 Books
__book: Book = None # 临时解析到的 Book
__current_element_name: str = None # 当前解析到的元素节点名称
def startDocument(self):
""" 文档开始 """
# 文档开始, 重置 books
self.books = []
def endDocument(self):
""" 文档结束 """
pass
def startElement(self, name, attrs):
""" 元素开始 """
# 元素开始, 保存当前元素节点名称
self.__current_element_name = name
if name == "book":
# <book/> 元素开始, 新建 Book 对象
self.__book = Book()
# 收集 <book/> 元素的属性值
self.__book.id = int(attrs.getValue("id"))
def endElement(self, name):
""" 元素结束 """
if name == "book":
# <book/> 元素结束, 保存收集到的 Book 对象
self.books.append(self.__book)
self.__book = None
# 元素结束, 清除当前元素节点名称
self.__current_element_name = None
def characters(self, content: str):
""" 文本节点 """
if self.__current_element_name == "name":
# 收集 <name/> 节点内的文本
self.__book.name = content.strip()
elif self.__current_element_name == "price":
# 收集 <price/> 节点内的文本
self.__book.price = float(content.strip())
if __name__ == "__main__":
# 创建 SAX 解析器
parser = xml.sax.make_parser()
# 创建用于解析 Books XML 的 Handler
handler = BooksContentHandler()
# 给解析器设置 Handler
parser.setContentHandler(handler)
# 解析 XML 文档
parser.parse("books.xml")
# xml.sax.parse("books.xml", handler) # 可以使用一个方法直接解析
# xml.sax.parseString(xmlstring, handler) # 可以直接解析一段 xml 文本
# 从 Handler 中获取解析结果输出
for book in handler.books:
print(book)
结果输出:
Book: id=123, name=Python, price=50.00
Book: id=456, name=Java, price=60.00
Book: id=789, name=C/C++, price=30.00
DOM 解析将整个 XML 文档一次性读入内存中解析成一课 DOM 树,再对树进行相关操作。
解析文件,文件名: books.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
<book id="123">
<name>Python</name>
<price>50.00</price>
</book>
<book id="456">
<name>Java</name>
<price>60.00</price>
</book>
<book id="789">
<name><![CDATA[C/C++]]></name>
<price>30.00</price>
</book>
</root>
DOM 解析 XML 代码示例:
import xml.dom.minidom
class Book:
"""
Book 类, 用于保存 xml 中 <book/> 元素节点的数据
"""
id: int = 0
name: str = None
price: float = 0.0
def __str__(self):
return "Book: id=%d, name=%s, price=%.2f" % (self.id, self.name, self.price)
if __name__ == "__main__":
# 解析 XML 文档, 返回一个文档对象(类型: xml.dom.minidom.Document)
doc = xml.dom.minidom.parse("books.xml")
# 获取文档的根元素(类型: xml.dom.minidom.Element)
root_elem = doc.documentElement
# 获取 root 元素下的所有 book 元素, 返回 元素的一个列表
book_elems = root_elem.getElementsByTagName("book")
books = []
# 遍历所有 <book/> 元素
for book_elem in book_elems:
book = Book()
# 获取 <book/> 元素的 ID 属性值
book.id = int(book_elem.getAttribute("id"))
# 获取 <book/> 元素下的 <name/> 元素
name_elem = book_elem.getElementsByTagName("name")[0]
# 获取 <name/> 元素下的 文本节点(类型: xml.dom.minidom.Text/xml.dom.minidom.CDATASection)
text_node = name_elem.childNodes[0]
# 获取 文本节点 的值
book.name = text_node.data.strip()
# 获取 <book/> 元素下的 <price/> 元素
price_elem = book_elem.getElementsByTagName("price")[0]
# 获取 <price/> 元素下的 文本节点(类型: xml.dom.minidom.Text/xml.dom.minidom.CDATASection)
text_node = price_elem.childNodes[0]
# 获取 文本节点 的值
book.price = float(text_node.data.strip())
books.append(book)
# 输出解析结果
for book in books:
print(book)
结果输出:
Book: id=123, name=Python, price=50.00
Book: id=456, name=Java, price=60.00
Book: id=789, name=C/C++, price=30.00