W weiserv
← 返回博客

网页长截图转 HTML+Markdown:从像素级还原到轻量内容提取的工具链与 5 个反直觉坑

技术教程

一句话结论

网页长截图转 HTML/Markdown 有两条路:像素级还原(重,曾做到叠差 1.75%)和轻量内容提取(默认,只看内容、基本 H5 排版)。关键是先定模式再动手——把高精度工具链硬套到"只要内容"的任务上,既费 token 又偏离目标。两条路共用一套"长图切片精读 + 重要配图按原坐标裁切"的资产。

场景与触发

在第三方知识库项目里要处理一批"生财有术"知识库长文截图(导航/选修篇系列,10+ 张),单张 01.找对标.png1294×9564px 的 PNG RGBA(2.57MB),正文里嵌 22 张演示图(微信搜一搜 / 看一看 / 微信读书 / 今日热榜 / 次幂 / 易撰等)。需求演变:第一版要"高度还原" → 用户三次反馈"太精细、浪费 token、专注内容、背景水印完全不要"。结论:绝大多数场景走轻量内容提取即可。

精确报错 / 关键输出(⭐ 最值钱的部分,原文照录)

坑 1:渲染视口宽度 ≠ 窗口参数

Edge 无头 --window-size=1336 时,实测 CSS 视口为 1312,1250 容器居中后左边界在 x=31

--window-size=1336 直接算 left 基准,图片会整体水平偏移 ~24px 且极难收敛。必须先渲染一次、量出容器左边界,再定 left 基准。

坑 2:全页纵向累积漂移

长页逐带渲染后,纵向偏移从 0 → −172px 累积漂移。靠增量补偿收敛:

δ_k = −(Δy_k − Δy_{k−1})

写回配置后重新生成图片层,循环 2–3 轮后 22 个图片锚点 Δy 全部归零。

坑 3:图片裁切截断(最隐蔽)

4 张裁切不完整:

  • img08 / img09 底部 tab 栏缺 +141px
  • img10 点赞栏缺 +84px
  • img03 / img22 顶部缺 12 / 17px

判据(铁律):资产边界外 2–4px 内仍有墨迹 ⇒ 疑似截断。必须用严格阈值(min < 200)复核,否则会把页面淡色水印误判为内容。只验证"记录坐标内像素一致"不够——那证明不了"裁切范围完整"。

坑 4:margin 折叠

.tip { margin-bottom: 57px } 会被后续 figure { margin-top: 21px } 折叠(取 max 57),导致调 figure 的 margin 完全无效

解决:改用相邻选择器 .tip + h3.sub { margin-top: 57px },不要依赖两个相邻元素的 margin 叠加。

坑 5:字体字宽差(物理上限)

原截图出自 macOS 苹方,本机 Windows 微软雅黑,长段断行位置差 2–5%。这是环境差异,不是代码缺陷——像素级还原需接受少数长段落断行微差。

解决过程与命令

实测原图(必做第一步):

python -c "from PIL import Image; im=Image.open('01.找对标.png'); print(im.size, im.mode)"
# (1294, 9564) RGBA

长图切片精读:切 700px 高、50px 重叠的条带逐片 1:1 读,不整图缩略(会漏红框标注)。

轻量模式(默认)工具链:

  • crop_figs.py:按 figs.json 坐标从原图裁重要配图到 assets/,命名 img01..imgNN
  • 生成 xxx.md(纯文本 Markdown)+ xxx.html(极简 H5:viewport 适配、朴素 CSS、无背景水印/绝对定位)

高精度模式(可选)工具链(14 个脚本,scripts/ 下):

  • stools.py 切片/取色/裁图 · inkmap.py 行段地图 · cmp_ink.py 行段对比
  • audit_edges.py/edge_refine.py 边界审计 · match_assets.py/match2d.py 锚点匹配
  • render_diff.py Edge 无头渲染+逐像素比对 · gen_figs.py 图片层 · calibrate.py 补偿闭环
  • final_preview.py 对照预览+差异热力图 · gen_markdown.py 同源 MD

验证结果(真实性自证)

  • 像素级实测指标:整体差异 1.75%(达 L3 <2%)、平均绝对差 2.75、22 个图片锚点 Δy=0、图片区域差异 0.0%
  • 环境实测:Python 3.13.12 + PIL 12.3.0 ✅、Edge 无头(msedge.exe)✅、playwright/pytesseract ❌(走视觉精读而非 OCR)
  • 轻量模式产出:同尺寸 22 图原像素复用 + 纯文本 MD + 极简 H5,零水印、零绝对定位

演示配图(差异热力图、22 张方法演示图拼图册、Edge 渲染 vs 对齐渲染对照)均为本地 build 产物,未随文发布;上述指标即对应这些配图的验收数据。

环境版本

  • OS:Windows 11 + WSL2(Ubuntu 22.04)
  • 语言/库:Python 3.13.12 + Pillow 12.3.0
  • 渲染:Microsoft Edge 无头(C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
  • 不可用:playwright、pytesseract(故文本提取走模型视觉精读,不依赖 OCR)

GEO 友好问答(AI 搜索可直引)

为什么网页长截图转 HTML 要分「像素级还原」和「轻量内容提取」两种模式?

因为目标不同:要 1:1 复刻视觉(含字体、间距、水印)才需要像素级还原;只要文字可编辑可搜索,轻量提取就够了。把高精度工具链硬套到"只要内容"的任务,既费 token 又偏离目标。绝大多数场景走轻量即可。

Edge 无头渲染的视口宽度为什么不等于 --window-size?

实测 --window-size=1336 时 CSS 视口是 1312:窗口边框/滚动条会吃掉约 24px。直接拿 1336 算 left 基准,整图会水平偏移约 24px 且难收敛。正确做法:先渲染一次、量出容器左边界,再定基准。

长页逐带渲染的纵向漂移怎么补偿?

逐带渲染会累积漂移(实测 0 → −172px)。用增量补偿 δ_k = −(Δy_k − Δy_{k−1}) 写回配置、重生成图片层,循环 2–3 轮即可让锚点 Δy 归零。

margin 折叠导致调 margin 无效怎么办?

相邻元素 margin 取 max 而非相加。.tipmargin-bottom 会被相邻 figuremargin-top 折叠。改用相邻选择器 .tip + h3.sub { margin-top: 57px },不要依赖两个相邻元素的 margin 叠加。

字体字宽差能消除吗?

不能彻底消除——这是物理上限。原图 macOS 苹方、本机 Windows 微软雅黑,长段断行差 2–5%。像素级还原需接受少数长段落断行微差;开工前就跟用户对齐"还原度 vs 代码质量"的取舍。

常见问题(FAQ)

Q:截图转 HTML 一定要像素级还原吗?
A:不一定。只要可编辑文字走轻量提取(零水印、零绝对定位);要 1:1 复刻视觉才上像素级还原(叠差可做到 1.75%)。

Q:Edge 无头视口宽度和 --window-size 对不上怎么办?
A:先渲染一次量出容器左边界,再定 left 基准;别直接拿窗口参数算。

Q:图片裁切怎么防止截断?
A:用边界审计:资产边界外 2–4px 内仍有墨迹即疑似截断,用严格阈值(min < 200)复核,别只验证"坐标内像素一致"。

给后来人的结论

  1. 先问"要复刻视觉还是只要内容"——默认轻量,别把高精度工具链当默认。
  2. 长图必切片;重要配图按原坐标 1:1 裁切复用;背景水印/装饰一律剥离。
  3. 高精度模式三道坎:视口宽度先量后算、margin 用相邻选择器、裁切后必做边界审计防截断。
  4. 字体字宽差是物理上限,断行微差不可免——这是"还原度 vs 代码质量"的取舍,开工前就跟用户对齐。
广告位占位 · post-inline