From 48dd02ce1d0c3fd8207522669c980a4f249a00b3 Mon Sep 17 00:00:00 2001
From: Bepr4 <63661977@qq.com>
Date: Sat, 22 Aug 2026 15:05:35 +0800
Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=2045=20=E4=BB=BD=20GovDoc=20?=
=?UTF-8?q?=E6=B5=8B=E8=AF=95=20Markdown=20=E7=9A=84=20HTML=20=E8=A1=A8?=
=?UTF-8?q?=E6=A0=BC=E5=8F=AA=E8=AF=BB=E7=BB=93=E6=9E=84=E5=88=86=E6=9E=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
新增 research-wiki/scratch/html-table-real-data-analysis-2026-08-21.md:
按展开 span 后行列一致性给 2474+356+1879 个顶层表格分类(剔除空 tr 后
简单表 53% / 合并合并表 8% / 损坏表 40%),定位标签不闭合、丢失
colspan 标注、无表头三类主因。README 当前阶段补对应条目。
---
README.md | 3 +
...tml-table-real-data-analysis-2026-08-21.md | 170 ++++++++++++++++++
2 files changed, 173 insertions(+)
create mode 100644 research-wiki/scratch/html-table-real-data-analysis-2026-08-21.md
diff --git a/README.md b/README.md
index b9ec0cb..b9af324 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,9 @@ profile 表达论文、政务文档、RAG、文档对比等不同需求。
(`research-wiki/scratch/html-table-cleaning-ecosystem-research-2026-08-21.md`):核实 Pandoc 表格
方言能力边界、Turndown 不处理合并单元格、Docling Markdown 导出重复合并单元格内容、MinerU 全
HTML 输出,印证审计 T001 的分流方向。
+- 2026-08-21 完成 45 份测试 Markdown 中 HTML 表格的只读结构分析
+ (`research-wiki/scratch/html-table-real-data-analysis-2026-08-21.md`):剔除空 `
` 后
+ 简单表 53% / 合法合并表 8% / 损坏表 40%,并定位标签不闭合、丢失 colspan 标注、无表头三类主因。
- 2026-08-21 仓库由 `govdoc-md-cleaner` 更名为 `mdpolish`,GitHub 远程仓库与本地目录同步改名;
冻结的 design 记录和带日期的 scratch 笔记保留当时的旧名。
diff --git a/research-wiki/scratch/html-table-real-data-analysis-2026-08-21.md b/research-wiki/scratch/html-table-real-data-analysis-2026-08-21.md
new file mode 100644
index 0000000..ff24e08
--- /dev/null
+++ b/research-wiki/scratch/html-table-real-data-analysis-2026-08-21.md
@@ -0,0 +1,170 @@
+# HTML 表格真实数据结构分析:45 份 GovDoc 测试 Markdown
+
+> 状态:只读分析记录,尚未进入任何 design。文中"建议"部分是候选方向,不是批准的方案。
+>
+> 分析日期:2026-08-21。
+>
+> 定位:用真实数据回答"我们的 HTML 表格到底长什么样、损坏在哪",为未来表格清洗 design 提供测量依据,
+> 并修正 [`html-table-cleaning-ecosystem-research-2026-08-21.md`](html-table-cleaning-ecosystem-research-2026-08-21.md)
+> 第 8 节中可以用数据回答的待决问题。
+
+## 1. 结论先行
+
+按"展开 rowspan/colspan 后每行列数是否一致"给全部顶层表格分类,**剔除空 `
` 之后**的分布:
+
+| 分类 | 定义 | 数量 | 占比 |
+|---|---|---|---|
+| A 简单矩形表 | 无合并单元格、每行列数一致、无嵌套、无游离内容 | 2474 | 53% |
+| B 合法合并表 | 有 rowspan/colspan,展开后仍是完整矩形 | 356 | 8% |
+| C 损坏表 | 参差网格、标签截断、占位冲突等 | 1879 | 40% |
+
+四个改变预期的发现:
+
+1. **一半的"损坏"是空 `
` 造成的假象**——剔除后 C 类从 54% 降到 40%;
+2. **标签不闭合会吞掉半篇文档**——最极端的一个未闭合表格吞了 2.79MB、1294 个表格;
+3. **参差网格与合并单元格强相关**——六成以上参差表带 span 属性,指向转换器丢失 colspan 标注;
+4. **无表头是常态**(A 类中 96%),**表格内图片为零**,单元格内管道符为零。
+
+## 2. 数据范围与方法
+
+- 输入:`/home/lihaoze/gov_test_data/compare/*/uploads/*.md`,45 份,全程只读;
+ 本文只含聚合数字和结构事实,不含任何原文片段;
+- 分析脚本在会话级临时目录 `/tmp/table-analysis/analyze.py`,未入库(仓库处于文档治理阶段,无源码目录);
+ 本文第 2.1 节的规则描述是复现依据;
+- 解析器:lxml `HTMLParser(recover=True)`。本地未装 html5lib。
+
+### 2.1 测量规则
+
+1. **代码围栏遮蔽**:先标记 ```` ``` ````/`~~~` 围栏内的位置,围栏里的 `` 不计入;
+2. **片段切分**:对围栏外的 ``
+ (无对应开启)单独计数并忽略;到文件尾仍未闭合的片段标记 `truncated`;
+3. **结构分析**:每个片段单独喂给 lxml 容错解析;对每个 `` 展开 rowspan/colspan 建二维占位网格,
+ 检测占位冲突,计算每行展开后的列数,收集表头、嵌套、游离文本、块级子标签、空单元格、超长单元格、
+ 退化重复等特征;
+4. **分类谓词**(`truncated` 为片段级标记,其余为表格级):
+
+ ```text
+ A_simple : 非 truncated 且 span_cells=0 且无嵌套 且每行展开列数一致
+ 且无占位冲突 且无游离文本 且块级子标签 ⊆ {br}
+ B_span_ok: 非 truncated 且无占位冲突 且每行展开列数一致 且无游离文本
+ C_broken : 其余全部
+ ```
+
+5. **空行修复复核**:把没有任何 `td`/`th` 子元素的 `` 整行剔除后重新跑同一分类,观察迁移。
+
+## 3. 总量与原始分类
+
+- 45 份文档共 7550 个 `` 259(5.5%)、
+单行表 157、截断片段 67、超长单元格(>500 字符)197、巨型片段(>10KB)70、退化重复内容 33、
+游离文本 27、占位冲突 14、嵌套表格 5、含块级标签 5、**含图片 0**。
+
+## 4. 发现一:空 `
` 是最大的单一"假损坏"来源
+
+完全没有 `td`/`th` 子元素的空行标签在参差表里出现 3000+ 行次。它们把"列数一致的好表"撑成
+"某些行 0 列"的参差表。
+
+剔除空行后重新分类:
+
+| 迁移路径 | 数量 |
+|---|---|
+| C_broken → A_simple | 553 |
+| C_broken → B_span_ok | 94 |
+
+即分类变为 **A 2474(53%)/ B 356(8%)/ C 1879(40%)**,一条零风险修复救回 13% 的表。
+空行不含任何内容,剔除是无损的。
+
+空行出现的位置:表中间 1151 处、表尾 385 处(对修复后仍为 C 的表统计),**没有出现在表头位置**——
+符合"转换器输出残留"而非"表头占位"的形态。
+
+## 5. 发现二:标签不闭合会吞掉后续正文
+
+最极端案例 `001-2/uploads/file_0_PDF.md`:全文件 1315 开 / 1124 闭;从第 1315 行开始的一个未闭合
+表格把后续 **2,789,092 字符、内部含 1294 个表格**的内容全部吞进一个"顶层片段"。
+
+全库 395 个闭合缺口意味着:**清洗的第一步不是处理表格,而是安全切分片段**。深度配对在标签缺失时
+会把正文和后续完整表格归并进一个巨型片段,后续所有基于片段的统计和修改都会失真。
+
+切分策略的可用锚点:相邻顶层表格之间,2943 对隔着真实文本、1661 对只隔空行——块边界
+(空行 + 后续正文)在实际数据中是可识别的。
+
+## 6. 发现三:参差网格与 span 强相关,指向"丢 colspan 标注"
+
+2506 个参差表中 **1624 个(65%)带 span 属性**——格子内容在、宽度信息没了,是转换器丢失
+colspan 标注的形态,不全是真缺内容。
+
+剔除空行后仍参差的 1851 个表,参差发生位置:
+
+| 位置 | 数量 | 推断成因 |
+|---|---|---|
+| 中间各行乱 | 780 | 真·结构损坏或逐行丢标注 |
+| 表头行比正文长 | 619 | 多级表头被拍平成一行、丢层级 |
+| 首行短 | 239 | 表头/首行缺格 |
+| 仅尾部截短 | 213 | 跨页截断尾巴 |
+
+四类成因不同,修复策略应当不同,但**都不能靠补空单元格自动修**——那正是
+[`../reference/GOVDOC_SAAS_CLEANING_SCOPE.md`](../reference/GOVDOC_SAAS_CLEANING_SCOPE.md) T002
+明确反对、且 rumdl MD056 的 auto-fix 方向被本仓库否决的做法。
+
+## 7. 发现四:无表头是常态,转换障碍集中在 `
`
+
+对修复后仍是 A 类的 2474 个表,检查转 GFM 管道表格的内容障碍:
+
+| 障碍 | 数量 | 占 A 类 |
+|---|---|---|
+| 无 `` 表头 | 2393 | 96% |
+| 单元格含 ` ` | 501 | 20% |
+| 单行表 | 176 | 7% |
+| 超长单元格(>500 字符) | 54 | 2% |
+| 单元格文本含 `\|` | 0 | 0% |
+| 表格内 ` ` | 0 | 0% |
+
+转义压力比预期小(管道符、图片都是零),压力集中在**合成表头**和 ` ` 处理上。
+
+## 8. 建议的处理策略(候选,未批准)
+
+三步走,对齐审计 T001 的分流方向,用真实数据修正边界:
+
+1. **切分**:容错定位 `` 片段;未闭合的在块边界截断并标记 truncated,
+ 绝不让深度配对吞正文;
+2. **无损修复**:只做剔除空 `` 这一级别的零风险修复(实测救回 13% 的表);
+3. **分流**:
+ - A 类(53%)→ 转 GFM:合成表头、` ` 转空格、管道符转义兜底;
+ - B 类(8%)→ 保留 HTML,规范化输出;
+ - C 类(40%)→ 不自动修,保留原样 + 按第 6 节四类成因报告分类原因,交人工确认或回源。
+
+对生态调研第 8 节待决问题的数据回答:
+
+- 问题 4(无表头表格怎么处理):数据表明无表头占绝对多数(96%),"补合成表头转 GFM"
+ 是主流路径;空表头还是首行充当表头仍是 design 待决;
+- 问题 1(简单/复杂分界线):分界线除了合并单元格,必须加"展开后网格是否矩形"——
+ 本数据中它是比 span 更强的损坏信号(53% 对 40%);
+- 问题 2、3(规范 HTML 程度、JSON schema)本次数据没有新增证据,维持开放。
+
+## 9. 局限
+
+- 全部数字只来自这 45 份文档,不得外推为一般结论(CLAUDE.md 第 5 节约束);
+- 53/8/40 依赖"剔除空 ` `"这条规则被采纳;不采纳则为 41/5/54;
+- lxml recover 解析可能自行重排损坏片段,个别表格的行列统计是解析结果而非字节事实;
+- 巨型片段内部的表格统计(如 001-2/file_0 被吞的 1294 个)已计入总数,但它们在原文中的
+ 真实边界未经人工核对;
+- 参差位置的四分类用的是简单规则(与列数众数比较),是启发式归类,不是语义判断。
+
+## 10. 验证状态
+
+- 第 3 至 7 节所有数字:2026-08-21 由只读脚本在真实数据上实际运行得出,脚本未入库;
+- 分类谓词与第 2.1 节规则描述和脚本逻辑一致,可据此重建等价测量;
+- 未验证:任何修复或转换策略的实际效果——A/B/C 分流、空行剔除、块边界截断都还没有实现,
+ 需要等对应 design 批准后用受控样本测试。
|