首页/开发/正则与编码速查/HTML 实体解码的常见陷阱

HTML 实体解码的常见陷阱

HTML 实体解码由解析器在词法阶段完成,常见坑包括二次解码、无分号命名实体、C1 区数字实体回退以及属性上下文差异;正确做法是只在数据入口用标准解析器解码一次,输出时按上下文重新编码。

正则与编码速查开发更新于 2026-10-17
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​​‌​‌‌‌‌​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​​​‌​‌‌​‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌‌‌​​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​​‌‌​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​

实体解码的边界

HTML 实体解码不是一次性的字符串替换,它由 HTML 解析器在词法阶段完成,并带有「无分号命名实体」「无效码位回退」等规定动作。多数解码 bug 的根源是把解码当成普通的查找替换,忽略了分号、上下文与实体表的覆盖范围。解码只应发生在数据读取的那一刻,并且只做一次。

基本信息

实体形式&name; 命名实体、&#十进制; 与 &#x十六进制; 数字实体
分号规则XML/XHTML 必须带分号;HTML 对历史命名实体允许缺分号,但后接字母数字或 = 时不生效
覆盖面HTML5 命名实体表条目规模上千,远大于 XML 的 5 个预定义实体
常见实现JS 的 DOMParser 或 he 库、Python html.unescape、Go html.UnescapeString
方向写入 HTML 前编码,读取时解码,两者不可互换

典型使用场景

  • 爬虫保存网页正文:把 &、中 还原成可读字符
  • 富文本编辑器回显:先解码再交给编辑器,避免用户看到 < 字面量
  • 日志与告警渲染:把接口返回的转义文本转成可读中文
  • 反查乱码:页面显示   说明被编码了两次
  • 数据迁移:从 HTML 备份里还原字段原文

可靠解码与校验

可靠解码与校验
// 交给浏览器解析器解码,不写 replace 链
const doc = new DOMParser().parseFromString(src, 'text/html');
const text = doc.documentElement.textContent;  // 已解码的纯文本
// 解码前先看有没有二次编码痕迹
const twice = /&(#\d+|#x[0-9a-f]+|[a-z]+);?/i.test(src);
// 反向操作:输出前按上下文编码
const enc = s => s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');

高频陷阱对照

现象原因规避
二次解码后冒出标签上游已解一次,本地又解一次,&amp;lt; 变成 <解码前检测 &amp; 痕迹,约定只在一层解码
&#128; 显示成 €HTML 解析器把 0x80-0x9F 数字实体按 Windows-1252 映射不要用数字实体表示 C1 控制区
&#x110000; 变�码位越界,解析器回退到 U+FFFD解码后校验码位范围
属性里 &quot; 撑破结构属性值中同样会做实体解码写入属性前按属性上下文单独编码
&nbsp 正常但 XML 报错缺分号,XML 不认XML/XHTML 里一律补分号
解码方向搞反就是 XSS 入口:把用户输入先解码再拼进 HTML,&#60;script&#62; 这类编码载荷会还原成真实标签。解码只用于读取文本,输出必须按新上下文重新编码。
Python 的 html.unescape 与 Go 的 html.UnescapeString 都比较宽松,缺分号的命名实体会被一并解码;要与浏览器行为严格一致时,宁可用浏览器解析器,并以 WHATWG HTML 标准的实体表为准。

易错点

  • 用正则链替换式解码,遇到 &amp;lt; 这类嵌套实体时不同顺序得到不同结果,应交给标准解析器一次解完
  • 只认 &...; 的手工实现漏掉无分号历史实体,页面里的 &copy 会原样保留,看似正常实则漏解
  • 解码后没有校验码位,非法字符或控制字符混入数据库,后续 XML/JSON 序列化时才报错
  • 在属性值中只编码引号而漏掉 & 与 <,属性仍可能被闭合,注入标签

数据来源与参考

  • WHATWG HTML Living Standard:字符引用与解析算法章节
  • MDN:DOMParser 与 textContent
  • Python 标准库文档:html.unescape

整理校对:其其网ICT文档中心 · 本页地址:docs.qiqicto.com/regex/html-entity-decode-pitfall.html

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​​‌​‌‌‌‌​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​​​‌​‌‌​‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌‌‌​​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​​‌‌​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​