当前位置:首页 > 文章列表 > 文章 > python教程 > Python xml.sax.handler.ContentHandler 怎样收集跨回调的文本内容

Python xml.sax.handler.ContentHandler 怎样收集跨回调的文本内容

来源:17golang原创 2026-10-27 13:51:55 0浏览 收藏

使用 Python 的 SAX 接口解析 XML 时,ContentHandler.characters() 收到的通常只是当前文本片段,不能假设一次回调就等于一个完整字段。稳定做法是:在目标元素开始时创建缓冲区,在每次 characters() 中追加字符串,等对应的 endElement() 到来后再合并。

官方文档:https://docs.python.org/3/library/xml.sax.handler.html

只要把“回调次数”和“字段边界”分开处理,就不会因为 SAX 把连续字符拆成多个片段而丢字或提前读取。普通元素用列表缓冲即可;有嵌套元素时,再为每个层级保留独立的收集状态。

characters() 为什么不能直接当成完整文本

Python 官方文档明确说明,解析器会用 characters(content) 报告字符数据,同一段连续字符既可能一次返回,也可能拆成多次返回。拆分并不代表 XML 字段被拆成了多个值,它只是 SAX 事件流的分片方式。

例如下面的 XML 中,标题的字符内容可能依次以“跨回”“回调收”“集文本”的片段到达。代码不能在第一次回调时就把变量当成最终标题,而要把片段按顺序放进列表。

SAX事件流将多个characters文本片段追加到缓冲区后合并成完整文本的结构图
图1:ContentHandler 事件与文本缓冲区关系说明图,展示跨回调合并思路,不是运行截图。

为目标元素维护文本缓冲区

最小实现只需要三个时机:startElement() 识别目标元素并清空缓冲区,characters() 追加当前片段,endElement() 负责拼接和保存结果。使用列表而不是每次都做字符串加法,可以让“收集片段”和“得到最终值”两个动作边界更清楚。

from io import StringIO
from xml.sax import make_parser
from xml.sax.handler import ContentHandler


class TextCollector(ContentHandler):
    def __init__(self):
        super().__init__()
        self._in_title = False
        self._parts = []
        self.titles = []

    def startElement(self, name, attrs):
        # 进入目标元素时重新开始收集,避免沿用上一个元素的片段。
        if name == "title":
            self._in_title = True
            self._parts.clear()

    def characters(self, content):
        # 一次回调只代表当前片段,空片段和空白也要由业务规则决定是否保留。
        if self._in_title:
            self._parts.append(content)

    def endElement(self, name):
        # 只有遇到目标元素的结束事件,才把所有片段合并成一个标题。
        if name == "title" and self._in_title:
            self.titles.append("".join(self._parts).strip())
            self._parts.clear()
            self._in_title = False


xml_text = """跨回调收集文本第二个标题"""

parser = make_parser()
handler = TextCollector()
parser.setContentHandler(handler)
parser.parse(StringIO(xml_text))

print(handler.titles)

这个处理器把 title 的边界交给 startElement/endElement,把字符的连续性问题交给列表。输出时才得到完整字符串;即使解析器把一项标题拆成多次回调,顺序仍然由 SAX 事件顺序保证。

嵌套元素怎样避免文本串错

当目标元素内部还有子元素时,单个全局列表就不够安全。例如

正文重点结尾 同时包含父元素文本和子元素文本。若在遇到 b 时覆盖父缓冲区,或者在任意 endElement 都立即清空,就会出现漏字和串层级的问题。

更稳妥的思路是保存当前元素栈:进入元素时压入一层状态,characters() 追加到栈顶,离开元素时先合并当前层,再决定是否把结果交给父层。下面的示例只收集带文本的元素,并把子元素文本按文档顺序并入父元素。

嵌套XML中父子元素分别使用缓冲区并在endElement时归并的层级结构图
图2:嵌套 XML 的层级缓冲结构图,说明父子元素如何分别收集文本,不是运行截图。
from io import StringIO
from xml.sax import make_parser
from xml.sax.handler import ContentHandler


class NestedTextCollector(ContentHandler):
    def __init__(self):
        super().__init__()
        self._stack = []
        self.values = {}

    def startElement(self, name, attrs):
        # 每个元素拥有自己的片段列表,避免父子元素共用可变状态。
        self._stack.append({"name": name, "parts": []})

    def characters(self, content):
        # 字符片段属于当前最深层元素,先追加到栈顶。
        if self._stack:
            self._stack[-1]["parts"].append(content)

    def endElement(self, name):
        # 先完成当前元素,再把它的文本顺序交给父元素。
        current = self._stack.pop()
        text = "".join(current["parts"])
        if name in {"title", "summary"}:
            self.values.setdefault(name, []).append(text.strip())
        if self._stack and text:
            self._stack[-1]["parts"].append(text)


xml_text = """正文重点结尾"""

parser = make_parser()
handler = NestedTextCollector()
parser.setContentHandler(handler)
parser.parse(StringIO(xml_text))

print(handler.values["summary"])

示例的关键不是把所有元素都保存成业务结果,而是保证每层的 characters() 片段都有归属。真实项目中可以只对需要的元素写入 values,对纯结构节点只负责转发文本。

把处理器交给 SAX 解析器

ContentHandler 是 SAX 的主要回调接口。应用通常继承它并覆盖感兴趣的方法,其余方法沿用默认实现。注册完成后,解析器会按照文档事件顺序调用处理器;因此不需要在 characters() 中猜测“下一次回调是不是同一字段”。

事件应该做什么不要做什么
startElement创建或压入当前元素的收集状态把当前状态当作最终文本
characters按到达顺序追加片段覆盖上一次片段
endElement合并当前层并按需交给父层无条件清空所有层级

空白、CDATA 和实体的处理边界

收集文本时是否调用 strip() 是业务决策,不是 SAX 的固定规则。标题或名称通常可以去掉首尾空白;代码片段、混排文本或需要保留缩进的内容则不能直接删除。CDATA 的字符内容同样会通过 characters() 报告,不能用“是否来自 CDATA”来替代缓冲策略。

另外,外部实体相关特性会影响实体事件是否进入应用,官方文档也提醒启用外部通用实体可能带来安全风险。本文只讨论普通字符收集,不建议为了收集文本而打开不需要的外部实体能力。

常见问题

为什么不能在 characters() 里直接赋值?

因为一次回调只承诺提供当前字符片段,后续片段可能仍属于同一个元素。直接赋值会覆盖之前收到的内容。

什么时候清空缓冲区?

进入同一个目标元素时清空一次,离开该元素并完成合并后再清空一次;嵌套结构则应清空对应栈层,不能清空整个全局状态。

父元素要不要重复保存子元素文本?

如果业务字段需要包含子元素的可见文本,就在子元素结束时按顺序把结果交给父层;如果父字段只需要直接文本,则应明确过滤子元素,避免重复记录。

小结

使用 ContentHandler 收集 XML 文本时,最重要的原则是“字符片段只追加,元素结束才定稿”。普通节点可以用一个列表,嵌套节点使用栈或按深度分层保存;最后再根据字段需求决定是否去掉空白、是否包含子元素文本。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python xml.dom.minidom.unlink 怎样释放不再使用的 DOM 树引用Python xml.dom.minidom.unlink 怎样释放不再使用的 DOM 树引用
上一篇
Python xml.dom.minidom.unlink 怎样释放不再使用的 DOM 树引用
Python wave.Wave_read 怎样读取 PCM 声道数和采样宽度
下一篇
Python wave.Wave_read 怎样读取 PCM 声道数和采样宽度
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    734次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    736次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    815次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    776次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    611次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码