浏览器静默跳过非法html实体并原样输出,不报错、不警告、不替换;仅当匹配合法实体格式(如、&等)时才解码,否则一律视为普通文本。

浏览器遇到非法 HTML 实体时到底做了什么
浏览器不会报错,也不会中断解析,而是静默跳过非法实体并保留原始字符串——比如 &xyz;、(超出 Unicode 范围)或 ≫(非法十六进制),全部原样输出为 &xyz;、、≫,不替换、不报错、不警告。
这种行为源于 HTML 解析器的状态机设计:只有在文本内容状态或属性值状态下,且匹配到合法的实体格式(如 、<code>>、&、"、' 或 {、{)时才执行替换;其余一律视为普通文本。
-
&后面没跟合法名称或数字 → 当作普通&字符处理 -
后不是十进制/十六进制有效数字 → 整个字符串保留原样 -
后含非十六进制字符(如G;)→ 停止解析,输出G; - 嵌套实体(如
<)→ 先解&得&,再剩lt;,最终显示为而非 <code>(即只解一层)
DOMParser 为什么无法识别非法实体错误
DOMParser().parseFromString(htmlStr, 'text/html') 永远返回一个 Document 对象,哪怕输入是 &xyz; 这种明显越界的实体。它和浏览器渲染引擎一样,不校验实体合法性,只做尽力解析。
这意味着你不能靠 DOMParser 来“检测”非法实体——它既不抛异常,也不在 body 里留空或标记错误节点。它的输出 DOM 结构可能和预期一致,但原始字符串里的非法实体依然躺在 textContent 里没被处理。
唯一可用的兜底判断是:doc.body.textContent.includes('&') 且已知输入不该含裸 &,才可疑;但更可靠的方式是主动扫描:
用正则:/&(?!(amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)/g
Python html.unescape() 的边界行为与容错建议
html.unescape() 能统一处理命名实体(如 ')和数字实体(如 é),但它对非法实体的处理取决于 Python 版本:
- Python 3.5+ 默认只转换合法实体;
&xyz;会被当作普通文本传入handle_data(),不触发错误回调 - 某些版本下
html.unescape('&xyz;')会直接抛ValueError,有些则静默返回原串 - 它没有内置的“验证钩子”,也没有
handle_invalid_entity类方法
安全做法是:先用 html.unescape(raw),再用 try/except ValueError 捕获失败;批量处理时,he 库比 html.unescape() 更鲁棒,能容忍 &ZZ; 等错误并返回原串。
lxml / BeautifulSoup 处理实体时的隐式宽容性
lxml 和 BeautifulSoup 底层依赖 libxml2 或 html5lib,对非法实体更宽容,但同样不报错——它们的目标是“还原可读”,不是“校验合规”。
例如:BeautifulSoup('&xyz;', 'html.parser').get_text() 会原样返回 &xyz;;而 soup.title.string 若含 😀,能正确解为 ?,但 仍保留。
关键点在于:这些库默认不暴露解析过程中的实体校验结果。若需强一致性(如内容审核场景),必须额外用 he 或自定义正则扫描 str(soup) 或 soup.encode().decode('utf-8') 输出。
真正容易被忽略的是:所有主流 HTML 解析器都把“实体合法性检查”让渡给了上层逻辑——它不是解析器的责任,而是你的责任。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











