应使用lxml库并显式声明目标元素为CDATA类型,因其能可靠获取未转义原始内容;标准ElementTree忽略CDATA结构,正则仅适用于简单稳定场景,minidom和BeautifulSoup均无法满足原始内容提取需求。

Python解析XML时CDATA内容被忽略或转义了怎么办
标准xml.etree.ElementTree默认把CDATA当作普通文本处理,不会保留结构,更不会阻止实体转义(比如<变成)。这不是bug,是设计使然——它只关心XML信息集,不保留标记语法细节。真要提取原始CDATA内容,得换思路。
用lxml配合parser.set_cdata_section_elements()
lxml是唯一在主流XML库中支持显式CDATA感知的方案。关键不是“读取CDATA标签”,而是告诉解析器:哪些元素的内容应被当作CDATA处理(即跳过实体解码、保留原始字符)。
- 必须提前知道哪些元素可能含CDATA(比如
description、script),无法全自动识别 - 需使用
lxml.etree.XMLParser,而非etree.parse()默认解析器 - 设置后,这些元素的
.text属性会返回未转义的原始字符串
from lxml import etree
<p>parser = etree.XMLParser()</p><h1>告诉解析器:所有 <content> 和 <pre class="brush:php;toolbar:false;"> 元素的内容按CDATA处理</h1><p>parser.set_cdata_section_elements(['content', 'code'])</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Python免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill7154" title="python-pro"><img
src="https://img.php.cn/upload/skill/000/000/081/179134208595348.jpg" alt="python-pro" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill7154" title="python-pro">python-pro</a>
<p>高级 Python 特性、异步编程、性能调优、静态类型、内存管理、Python 内部机制及生态库方面的专家。</p>
</div>
<a href="/xiazai/skill7154" title="python-pro" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p>xml_str = '''<root><content><![CDATA[<p>Hello & world</p>]]></content></root>'''
root = etree.fromstring(xml_str, parser)</p><p>print(root.find('content').text) # 输出:<p>Hello & world</p>(未转义,原样保留)</p>ElementTree里手动提取CDATA字符串(不推荐但有时可行)
如果只能用标准库,且XML格式固定、CDATA位置明确,可退而求其次:用正则从原始XML字符串中提取内容。这绕过了XML解析,风险高,但对简单场景够用。
- 仅适用于XML结构稳定、无嵌套CDATA、且你控制输入源的情况
- 正则
r'<!\[CDATA\[(.*?)\]\]>'会捕获最内层内容,注意re.DOTALL标志 - 无法处理CDATA跨行、或内容含
]]>的情况(XML本身禁止这样写,但坏数据可能有)
import re <p>xml_raw = '''<item><desc><![CDATA[Line1<br/>Line2]]></desc></item>''' match = re.search(r'<![CDATA[(.*?)]]>', xml_raw, re.DOTALL) if match: cdata_content = match.group(1) # 'Line1<br/>Line2'</p>
为什么不用minidom或BeautifulSoup
xml.dom.minidom虽能访问CDATASection节点,但其.data属性仍会做基础转义(如&→&),且API笨重;BeautifulSoup根本不是XML解析器,对CDATA无特殊处理,会直接丢弃标签或当普通文本。两者都不解决“获取未转义原始内容”这个核心诉求。
真正要可靠处理CDATA,lxml + 显式声明目标元素是目前最轻量也最可控的方式。别指望自动发现CDATA——XML标准本身不要求解析器暴露这种语法细节,所有方案都依赖你提前知道哪些字段该被当CDATA对待。

















