浏览器原生HTML解析器(如Blink)默认丢弃或标准化原始结构,包括:连续空白字符被合并、自闭合标签如<img/>被规范化为<img>、属性顺序不保留、IE条件注释<!--[if IE]>...<![endif]-->完全消失、<meta charset>若位置靠后则失效,且CDN/代理可能提前重写HTML。

HTML解析器默认会丢弃哪些原始结构
浏览器原生解析器(如Blink)在构建DOM树时,会主动忽略或标准化大量原始HTML文本中的“非语义”结构。比如:<!-- comment --> 被保留在DOM中但不进入渲染树;<script> 和 <style> 标签节点本身不参与布局,但内容影响样式计算;而像 <meta charset> 这类声明,如果出现在 <head> 后半段,Blink已按默认编码(ISO-8859-1)解析过前面内容,再补也无效——这不是bug,是词法分析阶段的不可逆行为。
保留原始结构的关键:绕过浏览器解析器
想拿到未经任何解析干预的原始HTML文本,必须跳过浏览器的HTML解析流程。最直接的方式是用 curl 或 requests.get() 直接获取HTTP响应体:
-
curl -s https://example.com输出的就是服务器返回的原始字节流,连换行缩进都原样保留 -
requests.get(url).text在Python中可用,但务必设response.encoding = response.apparent_encoding,否则中文可能乱码 - 别用
urllib.request.urlopen().read().decode("utf-8")硬解码——很多页面声明UTF-8,实际发的是GBK
注意:view-source:https://example.com 协议效果等同于Ctrl+U,也是原始响应体,但它受跨域限制,不能用于本地文件加载远程URL。
DOM树里还能找回多少“原始痕迹”
一旦HTML被Blink解析成DOM,部分原始结构就不可逆地丢失了。例如:
立即学习“前端免费学习笔记(深入)”;
- 连续多个空格、制表符、换行符会被合并为单个空白字符(除非CSS设了
white-space: pre) - 自闭合标签如
<img/>会被统一规范化为<img>,哪怕源码写的是<img /> - 属性顺序不保证保留:
<div id="a" class="b">和<div class="b" id="a">解析后在el.attributes中顺序可能不同 -
document.documentElement.outerHTML是当前DOM快照,不是原始源码——它包含JS动态插入的所有节点
所以,如果你依赖属性顺序、空白字符位置或标签书写风格做后续处理(比如模板比对、diff工具),必须在解析前就拿到原始字符串,而不是从DOM反推。
真正难保的:注释与条件编译结构
有些结构看似“只是注释”,实则承载业务逻辑。比如IE条件注释 <!--[if IE]><script>...</script><![endif]-->,现代浏览器解析时会直接跳过整个块,DOM里完全找不到痕迹;又比如Vue模板里的 <!-- template v-if="..." -->,本质是开发期占位符,运行时已被移除。这类内容无法靠配置开关保留,只能在服务端或抓取阶段截获原始响应并单独提取。
最容易被忽略的是:你拿到的“原始HTML”可能早已被CDN或反向代理重写过——比如自动注入性能监控脚本、改写图片src为WebP格式、甚至删掉注释以减小体积。验证方式很简单:用 curl -v 查看响应头里的 Content-Length 和实际保存文件大小是否一致。



















