Files
mdpolish/research-wiki/scratch/html-table-cleaning-ecosystem-research-2026-08-21.md
T
Bepr4 8ac4f1dd12 更名 mdpolish 并补充清洗流水线设计与调研记录
- 仓库由 govdoc-md-cleaner 更名为 mdpolish,更新 README、AGENTS、CLAUDE
  及 reference 中的仓库名;冻结的 design 与带日期 scratch 保留旧名
- 冻结 0002:可组合清洗组件与流水线(check/transform、单轮修改加最终复查)
- 新增 0003 草稿:第一版可执行核心架构,待评审
- 新增 HTML 表格清洗专题调研(2026-08-21)
2026-08-21 22:49:03 +08:00

9.4 KiB
Raw Blame History

HTML 表格清洗专题调研:业界工具在 Markdown 清洗中如何处理 HTML 表格

状态:调研记录,尚未进入任何 design。

调研日期:2026-08-21。

定位:回答一个具体问题——Markdown 清洗中遇到 HTML 表格,业界工具实际怎么做。 结论用于印证或修正 ../reference/GOVDOC_SAAS_CLEANING_SCOPE.md 中 T001/T002 的方向,不构成对任何方案的批准。

1. 结论先行

围绕“Markdown 里的 HTML 表格怎么办”,生态里的工具分成三种流派:

流派 做法 代表
生成端保真 复杂表格直接输出 HTML,不做管道表格 MinerU、DoclingHTML/JSON 视图)
强行归一 全部转成管道表格,合并单元格静默损坏或内容重复 Turndown + gfm 插件、DoclingMarkdown 视图)
保真派 容错解析 → 校验网格 → 简单表转 GFM、复杂表保留 HTML/JSON 本项目审计 T001 方向、Pandocgrid 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"。

来源:Pandoc MANUAL - Tables

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 年发布后基本未改)只做两件事:

  1. 首行不是全 <th>(无表头行)的表格:保留 HTML 不转;
  2. 其余表格:按 DOM 位置逐格输出管道符。

完全没有 colspan/rowspan 的处理代码。合并单元格不触发上面的回退,直接按 DOM 位置压扁, 转出列数不齐的坏表,且不报任何错

对本项目的含义:

  • “无表头就不转”是能力判断驱动的回退,这个思想是对的;但它的能力判断漏掉了合并单元格;
  • 连最流行的转换库在这里都会静默弄坏表格——审计要求“先建 DOM、校验网格、禁止正则替换”有真实事故支撑;
  • 选 HTML→Markdown 转换库时,“是否处理 span”必须列入验证项,不能信 README 宣称。

5. 反面教材二:Docling 的 Markdown 导出重复合并单元格内容

读了 docling-core 2.92.0 的源码(wheel 解包,2026-08-21):

  • 内部 TableCellrow_span/col_span 和起止行列偏移;TableData.grid 属性把同一个 cell 对象 铺满它覆盖的每个 (行, 列) 位置;
  • HTML 序列化器transforms/serializer/html.py):遍历网格时跳过被覆盖的续位 rowstart != icolstart != jcontinue),只在起始位置输出,并正确带上 rowspan="N"/colspan="N"——语义保真;
  • Markdown 序列化器transforms/serializer/markdown.pyMarkdownTableSerializer): 直接遍历铺满后的 grid,每个位置都输出 col.text——一个 row_span=3 的单元格内容在 Markdown 输出里重复出现 3 次。转义只处理换行和管道符(\n→空格、|&#124;),再用 tabulate tablefmt="github" 输出管道表格。

即 Docling 面对“Markdown 视图必须有合并单元格”的需求,选择了内容重复来保住矩形形状。 这是“强行转管道表格会丢语义”的又一个实例,和 Turndown 的压扁是同一根源的两种表现。

对本项目的含义:

  • “转 GFM”不是免费的格式变换,每一家实现都发明了自己的有损映射;
  • 如果未来用 Docling 做回源提取(调研报告第 6.1 节的候选方向),它的 Markdown 导出不能直接当作 保真输出使用,需要用它的 DoclingDocument JSON 或 HTML 视图;
  • 审计 T001 说“强行转 GFM 会丢失语义”,这里的机制证据是:跨行列单元格要么被压扁(Turndown)、 要么被重复(Docling)、要么失去合并关系本身(都失去 rowspan/colspan 语义)。

6. 清洗与格式化工具:主流选择是“不动 HTML 块”

工具 对 Markdown 内 HTML 表格的行为 来源
remark / mdformat raw HTML 当不透明块原样传递,不重新格式化、不转换 mdformat 官方文档(核心保证是格式化前后 AST 一致,HTML 块不在处理范围)
rumdl MD033no-inline-html 报告 <table>(它有 Markdown 等价物),但 fix 只自动转 em/strong/code/a/img/br/hr 等行内简单标签,不含表格allowed-inside = ["table"] 可整块豁免 本地 reference/rumdl/docs/md033.md
rumdl MD056table-column-count 校验每行列数与表头一致,自动修复方式是补/删空单元格 本地 reference/rumdl/docs/md056.md
rumdl MD058blanks-around-tables GFM 表格前后补空行 本地 reference/rumdl/docs/md058.md

两点值得注意:

  1. 没有主流工具自动把 HTML 表格转成 GFM 表格。 连以“消灭 HTML”为目标的 MD033 都把表格留在 “只报告、不修复”的范围里——因为工具作者知道这个转换会弄坏表格。
  2. 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 时需要回答,本调研只提供背景:

  1. 简单/复杂表格的分界线,除了“有无合并单元格”,是否还要看单元格内块级元素、嵌套表格和表头层级;
  2. 复杂表保留的“规范 HTML”具体规范到什么程度(属性白名单?标签重排?缩进策略?);
  3. JSON grid 的格式是否对齐 Docling 的 TableCellrow_span/col_span/offset 字段), 还是自定义 schema——涉及与未来回源 adapter 的成本权衡;
  4. 无表头表格(Turndown 的回退条件)按哪种流派处理:补合成表头转 GFM,还是保留 HTML。

9. 验证状态

  • Pandoc 手册、Turndown 源码、MinerU README、Docling 官网:2026-08-21 通过网络核实;
  • docling-core 2.92.0:下载 wheel 解包读源码核实,涉及 MarkdownTableSerializer.serializeTableData.grid、HTML 序列化器的 span 处理;
  • rumdl 三条规则:读本地 reference/rumdl/docs/(该目录为镜像副本,以 rumdl 上游为准);
  • 未验证:各工具在本项目真实数据上的实际表现——需要等对应组件 design 批准后用受控样本测试。