BeautifulSoup默认无法解析非标准XML,因其xml解析器严格遵循规范;需先用lxml.etree.XMLParser(recover=True)容错解析,再转字符串交由BeautifulSoup处理。

为什么 BeautifulSoup 默认无法正确解析非标准 XML
BeautifulSoup 的 xml 解析器(即 lxml-xml 或 xml)严格遵循 XML 规范,遇到常见非标准情况会直接报错或丢弃内容。比如:自闭合标签未写斜杠(<tag></tag> 而非 <tag></tag>)、属性值无引号(<tag attr="value"></tag>)、缺失根节点、含非法字符实体(&foo;)、或混用 HTML 实体( )——这些在真实爬虫/遗留系统中极常见,但 lxml.etree.XMLParser(recover=True) 也不买账。
改用 lxml 的 recover parser + BeautifulSoup 的组合方案
核心思路是:不把原始文本直接喂给 BeautifulSoup(..., 'xml'),而是先用 lxml.etree.fromstring() 带容错解析,再把修复后的树转成字符串交给 BeautifulSoup 处理。这样既能保留 lxml 的恢复能力,又不放弃 BeautifulSoup 熟悉的 API。
-
lxml.etree.XMLParser(recover=True)可容忍多数语法错误,如缺失引号、未闭合标签、乱码实体 - 必须显式传入
parser参数,否则fromstring()仍走严格模式 - 解析后调用
etree.tostring(tree, encoding='unicode', method='xml')转回字符串,避免字节/编码问题 - 再用
BeautifulSoup(..., 'xml')加载该字符串,后续操作完全不变
from lxml import etree
from bs4 import BeautifulSoup
<p>broken_xml = '<root><item id=123>text</item><meta name=author content=John></root>'</p><h1>关键:启用 recover 模式</h1><p>parser = etree.XMLParser(recover=True)
tree = etree.fromstring(broken_xml.encode('utf-8'), parser)
fixed_xml = etree.tostring(tree, encoding='unicode', method='xml')</p><p>soup = BeautifulSoup(fixed_xml, 'xml')
print(soup.find('item')['id']) # 输出:123遇到命名空间或 CDATA 时的特殊处理
非标准 XML 常混用命名空间前缀(如 <ns:tag>)却不声明 xmlns:ns,或把 JSON/JS 代码塞进 <![CDATA[...]]> 却漏写右括号。BeautifulSoup 对 CDATA 默认当普通文本,而 lxml 的 recover parser 会把未闭合的 CDATA 当注释吞掉。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
立即学习“Python免费学习笔记(深入)”;
- 若需保留 CDATA 内容原样,解析前先用正则临时替换:
re.sub(r'<!\[CDATA\[', '<__CDATA_START__', xml_str),解析完再还原 - 命名空间报错(如
Namespace prefix ns not declared)可提前移除所有xmlns:属性和带冒号的标签名,用re.sub(r'xmlns:[^=]+="[^"]*"', '', xml_str) - lxml 的
recover=True对<
















