- 仓库由 govdoc-md-cleaner 更名为 mdpolish,更新 README、AGENTS、CLAUDE 及 reference 中的仓库名;冻结的 design 与带日期 scratch 保留旧名 - 冻结 0002:可组合清洗组件与流水线(check/transform、单轮修改加最终复查) - 新增 0003 草稿:第一版可执行核心架构,待评审 - 新增 HTML 表格清洗专题调研(2026-08-21)
9.4 KiB
HTML 表格清洗专题调研:业界工具在 Markdown 清洗中如何处理 HTML 表格
状态:调研记录,尚未进入任何 design。
调研日期:2026-08-21。
定位:回答一个具体问题——Markdown 清洗中遇到 HTML 表格,业界工具实际怎么做。 结论用于印证或修正
../reference/GOVDOC_SAAS_CLEANING_SCOPE.md中 T001/T002 的方向,不构成对任何方案的批准。
1. 结论先行
围绕“Markdown 里的 HTML 表格怎么办”,生态里的工具分成三种流派:
| 流派 | 做法 | 代表 |
|---|---|---|
| 生成端保真 | 复杂表格直接输出 HTML,不做管道表格 | MinerU、Docling(HTML/JSON 视图) |
| 强行归一 | 全部转成管道表格,合并单元格静默损坏或内容重复 | Turndown + gfm 插件、Docling(Markdown 视图) |
| 保真派 | 容错解析 → 校验网格 → 简单表转 GFM、复杂表保留 HTML/JSON | 本项目审计 T001 方向、Pandoc(grid tables / AST) |
支撑这张表的共同事实是:GFM 管道表格语法在原理上表达不了合并单元格。三种流派只是对这条约束的 不同回答——绕开它、硬转它、或者按能力分流。
2. 语法能力边界:GFM 管道表格没有合并单元格写法
Pandoc 手册对各家 Markdown 表格方言的原文描述(2026-08-21 从官方 MANUAL 核实):
| 方言 | 合并单元格 | 单元格内块级元素 |
|---|---|---|
| pipe tables(≈GFM 表格) | 不支持(单元格不能跨多行) | 不能包含块级元素 |
| multiline tables | 明确不支持跨行/跨列单元格 | 可以 |
| grid tables | 支持("Cells can span multiple columns or rows") | 可以 |
HTML <table> |
原生 rowspan/colspan |
可以 |
grid tables 是唯一支持合并单元格的 Markdown 表格语法,但它是 Pandoc 扩展,GitHub 不渲染, 对“清洗后还要在 GFM 渲染器里查看”的场景不可用。所以在 GFM 方言内部,合并单元格没有任何无损写法; 想保真只能保留 HTML,或存结构化 JSON。
Pandoc 手册同时警告:从表达能力更强的格式转换时"some document elements, such as complex tables, may not fit","can be expected to be lossy"。
3. 生成端:PDF→Markdown 转换器为什么输出 HTML 表格
MD 文档里出现 HTML 表格,通常不是 bug,而是转换器面对合并单元格(rowspan/colspan)、
多级表头等管道语法表达不了的结构时的标准回退。
| 工具 | 表格输出策略 | 依据 |
|---|---|---|
| MinerU | 所有表格一律输出 HTML 嵌在 Markdown 中,不做管道表格;支持跨页表格拼接 | 官方 README 功能列表 |
| Docling | 内部 TableFormer 模型专门恢复合并单元格;同一文档可导出 HTML / Markdown / JSON 三种视图 | 官网能力页 + docling-core 2.92.0 源码 |
对本项目的含义:HTML 表格是合法的中间形态,不是待清除的垃圾。清洗目标不是“消灭 HTML 表格”, 而是“识别哪些表格结构正确、哪些在转换中损坏”。
4. 反面教材一:Turndown 静默产出错位表格
Turndown 是最流行的 HTML→Markdown 转换库之一, turndown-plugin-gfm 的 tables.js (v1.0.2,2018 年发布后基本未改)只做两件事:
- 首行不是全
<th>(无表头行)的表格:保留 HTML 不转; - 其余表格:按 DOM 位置逐格输出管道符。
它完全没有 colspan/rowspan 的处理代码。合并单元格不触发上面的回退,直接按 DOM 位置压扁,
转出列数不齐的坏表,且不报任何错。
对本项目的含义:
- “无表头就不转”是能力判断驱动的回退,这个思想是对的;但它的能力判断漏掉了合并单元格;
- 连最流行的转换库在这里都会静默弄坏表格——审计要求“先建 DOM、校验网格、禁止正则替换”有真实事故支撑;
- 选 HTML→Markdown 转换库时,“是否处理 span”必须列入验证项,不能信 README 宣称。
5. 反面教材二:Docling 的 Markdown 导出重复合并单元格内容
读了 docling-core 2.92.0 的源码(wheel 解包,2026-08-21):
- 内部
TableCell带row_span/col_span和起止行列偏移;TableData.grid属性把同一个 cell 对象 铺满它覆盖的每个 (行, 列) 位置; - HTML 序列化器(
transforms/serializer/html.py):遍历网格时跳过被覆盖的续位 (rowstart != i或colstart != j时continue),只在起始位置输出,并正确带上rowspan="N"/colspan="N"——语义保真; - Markdown 序列化器(
transforms/serializer/markdown.py的MarkdownTableSerializer): 直接遍历铺满后的 grid,每个位置都输出col.text——一个row_span=3的单元格内容在 Markdown 输出里重复出现 3 次。转义只处理换行和管道符(\n→空格、|→|),再用 tabulatetablefmt="github"输出管道表格。
即 Docling 面对“Markdown 视图必须有合并单元格”的需求,选择了内容重复来保住矩形形状。 这是“强行转管道表格会丢语义”的又一个实例,和 Turndown 的压扁是同一根源的两种表现。
对本项目的含义:
- “转 GFM”不是免费的格式变换,每一家实现都发明了自己的有损映射;
- 如果未来用 Docling 做回源提取(调研报告第 6.1 节的候选方向),它的 Markdown 导出不能直接当作
保真输出使用,需要用它的
DoclingDocumentJSON 或 HTML 视图; - 审计 T001 说“强行转 GFM 会丢失语义”,这里的机制证据是:跨行列单元格要么被压扁(Turndown)、
要么被重复(Docling)、要么失去合并关系本身(都失去
rowspan/colspan语义)。
6. 清洗与格式化工具:主流选择是“不动 HTML 块”
| 工具 | 对 Markdown 内 HTML 表格的行为 | 来源 |
|---|---|---|
| remark / mdformat | raw HTML 当不透明块原样传递,不重新格式化、不转换 | mdformat 官方文档(核心保证是格式化前后 AST 一致,HTML 块不在处理范围) |
| rumdl MD033(no-inline-html) | 报告 <table>(它有 Markdown 等价物),但 fix 只自动转 em/strong/code/a/img/br/hr 等行内简单标签,不含表格;allowed-inside = ["table"] 可整块豁免 |
本地 reference/rumdl/docs/md033.md |
| rumdl MD056(table-column-count) | 校验每行列数与表头一致,自动修复方式是补/删空单元格 | 本地 reference/rumdl/docs/md056.md |
| rumdl MD058(blanks-around-tables) | GFM 表格前后补空行 | 本地 reference/rumdl/docs/md058.md |
两点值得注意:
- 没有主流工具自动把 HTML 表格转成 GFM 表格。 连以“消灭 HTML”为目标的 MD033 都把表格留在 “只报告、不修复”的范围里——因为工具作者知道这个转换会弄坏表格。
- MD056 的自动修复方向与本项目审计相反。 审计 T002 反对“补空单元格凑齐列数通过语法检查”, 因为这可能掩盖静默丢列;MD056 恰恰把补空作为修复手段。借用这类规则时必须关掉它的自动修复, 只取检测部分。
7. 与既有材料的关系
- 审计 T001(
../reference/GOVDOC_SAAS_CLEANING_SCOPE.md第 4.4 节)的五步法——容错解析建 DOM、 展开 rowspan/colspan 校验二维网格、简单矩形表转 GFM / 复杂表保留 HTML 或 JSON、拆多行、 回源确认幻觉——与本次调研的所有正面证据一致,未发现需要修正的点; markdown-cleaning-ecosystem-research-2026-08-20.md第 5 节的推荐流程(html5lib 容错解析、禁正则、按合并单元格分流)同样得到印证;- 新增的证据是反面案例的具体机制:Turndown 的压扁路径、Docling Markdown 视图的重复路径、 Pandoc 手册的方言能力原文、MD056 修复方向与审计相反。
8. 对本项目的待决问题(不是结论)
以下问题在对应 design 时需要回答,本调研只提供背景:
- 简单/复杂表格的分界线,除了“有无合并单元格”,是否还要看单元格内块级元素、嵌套表格和表头层级;
- 复杂表保留的“规范 HTML”具体规范到什么程度(属性白名单?标签重排?缩进策略?);
- JSON grid 的格式是否对齐 Docling 的
TableCell(row_span/col_span/offset 字段), 还是自定义 schema——涉及与未来回源 adapter 的成本权衡; - 无表头表格(Turndown 的回退条件)按哪种流派处理:补合成表头转 GFM,还是保留 HTML。
9. 验证状态
- Pandoc 手册、Turndown 源码、MinerU README、Docling 官网:2026-08-21 通过网络核实;
- docling-core 2.92.0:下载 wheel 解包读源码核实,涉及
MarkdownTableSerializer.serialize、TableData.grid、HTML 序列化器的 span 处理; - rumdl 三条规则:读本地
reference/rumdl/docs/(该目录为镜像副本,以 rumdl 上游为准); - 未验证:各工具在本项目真实数据上的实际表现——需要等对应组件 design 批准后用受控样本测试。