xml.sax.make_parser() 返回一个未配置的解析器对象,需手动设置contenthandler才能调用parse(),否则因handler为none而抛attributeerror。

xml.sax.make_parser() 返回什么,为什么不能直接 parse()?
xml.sax.make_parser() 返回的是一个解析器对象(比如 xml.sax.expatreader.ExpatParser 实例),不是“开箱即用”的解析器——它默认没注册任何内容处理器(ContentHandler)。如果你直接调 parser.parse() 而没设 setContentHandler(),会抛 AttributeError: 'NoneType' object has no attribute 'startElement'。
常见错误现象:代码跑起来就报错,但看不出哪行漏了;或者解析静默完成、什么回调都没触发。
- 必须显式创建并设置一个继承自
xml.sax.handler.ContentHandler的类实例 - 不能只靠
make_parser()就认为“解析器已就绪” - 部分 Python 版本(如 3.9+)在未设 handler 时会抛更明确的
TypeError,但逻辑不变
怎么写一个最小可用的 ContentHandler?
你不需要实现全部方法。SAX 只在遇到对应事件时才调用对应方法,只实现你关心的即可。比如只提取所有 <title></title> 文本,只需重写 startElement()、characters() 和 endElement()。
容易踩的坑:characters() 可能被多次调用(比如文本跨 CDATA 或实体),不能直接赋值,得用 += 累积;startElement() 的 attrs 是 xml.sax.xmlreader.AttributesImpl,支持 get() 和 keys(),但不支持字典解包或 .items() 直接遍历(老版本会报错)。
立即学习“Python免费学习笔记(深入)”;
class TitleHandler(xml.sax.handler.ContentHandler):
def __init__(self):
self.in_title = False
self.title_text = ""
def startElement(self, name, attrs):
if name == "title":
self.in_title = True
def characters(self, content):
if self.in_title:
self.title_text += content.strip()
def endElement(self, name):
if name == "title":
self.in_title = False
print("Found title:", self.title_text)
self.title_text = ""
解析失败时怎么定位是 XML 还是代码问题?
SAX 解析出错通常分两类:SyntaxError(XML 格式非法)和 ExpatError(底层 expat 解析器报错),后者会带行号列号,比如 xml.parsers.expat.ExpatError: not well-formed (invalid token) at line 12, column 5。
这时候别急着改 Python 代码——先用命令行快速验证 XML:
-
python -m xml.etree.ElementTree your_file.xml(轻量检查格式) -
xmllint --noout your_file.xml(需安装 libxml2,更严格) - 注意 BOM、编码声明(
<?xml version="1.0" encoding="UTF-8"?>)是否与文件实际编码一致;Python 3 默认按 UTF-8 打开,若文件是 GBK 且无声明,parse()会直接报UnicodeDecodeError
要不要用 make_parser()?还是直接 xml.sax.parse() 更简单?
绝大多数场景下,直接用 xml.sax.parse() 更安全、简洁。它内部自动调 make_parser() + setContentHandler(),省去手动创建解析器对象的步骤。
只有当你需要定制解析器行为时,才绕不开 make_parser(),例如:
- 替换底层驱动(比如用
xml.sax.drivers2.drv_pyexpat,极少用) - 提前设置
setFeature()(如关闭命名空间处理:parser.setFeature(xml.sax.handler.feature_namespaces, 0)) - 设置
setProperty()(如http://xml.org/sax/properties/lexical-handler处理注释/CDATA)
性能上没差别,但多一层对象管理就多一层出错可能。新手从 xml.sax.parse(filename, handler) 入手,等真遇到需要干预解析过程时,再回头碰 make_parser()。
最常被忽略的一点:SAX 是单次流式解析,handler 实例不能复用——每次解析新文件都要新建 handler 对象,否则状态(比如 self.in_title)会残留。







