当前位置: 首页 > 工具软件 > SAX for .NET > 使用案例 >

Python3: XML 的 SAX 和 DOM 解析

孙渝
2023-12-01

本文链接: https://blog.csdn.net/xietansheng/article/details/115559132

Python3 学习笔记(目录)

0. XML 简介

常用的 XML 解析有 SAX 和 DOM 两种解析方式(还有 PULL 方式)。

SAX 解析以事件驱动方式一边读取一边解析 XML 文档,DOM 则将整个 XML 文档读取到内存中形成一颗 DOM 树后再进行操作。

Python 标准库中包含了 XML 的 SAX 和 DOM 解析。

Python Docs:

1. SAX(Simple API for XML)

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

2. DOM(Document Object Model)

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
 类似资料: