PDF表格解析复杂场景知识沉淀
2026/6/26 7:50:07 网站建设 项目流程

PDF 表格解析复杂场景知识沉淀

适用于政府报告、统计月报、监测数据等结构化 PDF 的自动化提取(对上一篇PDF解析知识的拓展)


一、单元格合并的三种形态

合并类型视觉表现PDF 底层真相处理策略
行内合并(横向)一个单元格跨多列该位置只画一次文字,无竖线分隔检测列边界缺失,将多列内容合并
列内合并(纵向)一个单元格跨多行该位置只在第一行画文字,后续行该列为空检测 Y 坐标连续性,向下填充直到新记录开始
行列交叉合并大块区域合并既无横线也无竖线,文字只出现一次结合 X/Y 双向检测,标记为合并区域

关键难点:PDF 没有"合并单元格"的语义,只有"某些位置没画文字"。程序无法区分是"合并"还是"数据缺失"。


二、单元格内容多行(最隐蔽的问题)

2.1 视觉表现

一个单元格里文字换行了,比如:

┌─────────┐ │ 贵阳市 │ │ 南明区 │ │ 云岩区 │ └─────────┘

2.2 PDF 底层

实际上是 3 个独立的文字块,Y 坐标不同,但被框在同一个矩形内。

2.3 处理策略

  1. 第一步:先按extract_words提取所有文字块,记录每个块的(x0, y0, x1, y1)
  2. 第二步:判断这些块是否属于同一个单元格 —— 看它们的 X 范围是否高度重叠(重叠度 > 80%)
  3. 第三步:同一单元格内的多行内容,用换行符\n拼接,或按业务规则合并(如"贵阳市\n南明区" → “贵阳市南明区”)

场景关联:水源地名称可能出现"原北郊水库\n(备用)"这种多行备注,需要识别为同一个单元格。


三、跨页表格的处理

3.1 跨页的两种模式

模式特征处理策略
续表模式第二页有"续表"或"接上页"字样,有表头检测到"续表"关键词后,将两页数据拼接,表头去重
无标识续页第二页直接继续数据,无表头无标识用记录边界检测(序号连续性)判断是否为同一表格的延续

3.2 跨页后表头的四种情况

情况示例处理策略
每页都有完整表头第1页有表头,第2页也有表头提取时识别表头行,去重或跳过
仅首页有表头第1页有表头,第2页直接是数据用首页表头做列定位,后续页直接按坐标对齐
续页有特殊表头第2页表头简化为"续上表:XX月报"正则匹配"续"字,跳过该行
完全无表头第2页直接是数据,无任何标识最困难,需要用内容特征推断列语义

3.3 跨页数据拼接的关键逻辑

defmerge_cross_page_tables(pages_data):""" 跨页表格合并 """all_records=[]last_seq=0forpage_idx,page_recordsinenumerate(pages_data):ifnotpage_records:continue# 检测是否为新表格开始(有序号重置)first_seq=page_records[0].get("序号")iffirst_seqandfirst_seq<=last_seq:# 序号重置 = 新表格,不拼接all_records.extend(page_records)else:# 序号连续 = 续表,拼接all_records.extend(page_records)# 更新最后序号ifpage_records:last_seq=page_records[-1].get("序号",0)returnall_records

四、综合处理框架

PDF 输入 │ v [Step 1] 逐页提取文字块 (extract_words) │ └── 记录每个词的 (text, x0, y0, x1, y1, page_num) │ v [Step 2] 检测表格区域 │ ├── 有边框 → 用 lines 策略检测单元格边界 │ └── 无边框 → 用文字对齐 + 空白间隙推断列边界 │ v [Step 3] 单元格重建 │ ├── 单格单词 → 直接赋值 │ ├── 单格多词(同行)→ X 重叠检测,合并为同一单元格 │ ├── 单格多行(同列)→ Y 连续性检测,用 \n 拼接 │ └── 合并单元格 → 标记为 merged,向下/向右填充 │ v [Step 4] 记录边界检测 │ ├── 有序号 → 新记录开始(最可靠) │ ├── 有城市名 → 新记录开始 │ └── 无标识 → 上一条记录的续行(合并到上一条) │ v [Step 5] 跨页处理 │ ├── 检测"续表"/"接上页"关键词 │ ├── 检测序号连续性(1,2,3... 不中断) │ └── 拼接数据,去除重复表头 │ v [Step 6] 后处理 ├── 合并单元格内容填充(向下/向右) ├── 多行内容合并(按业务规则) └── 输出结构化数据

五、关键代码片段

5.1 检测单元格内多行内容

defgroup_words_into_cells(words,col_boundaries):""" 将文字块按单元格分组,处理多行内容 """cells={}forwordinwords:# 找到该词属于哪一列col_idx=find_column(word["x0"],col_boundaries)# 找到该词属于哪一行(用锚点对齐,非简单Y聚类)row_key=find_row_anchor(word,words)key=(row_key,col_idx)ifkeynotincells:cells[key]=[]cells[key].append(word)# 同一单元格内的词按 Y 排序,拼接result={}for(row,col),word_listincells.items():word_list.sort(key=lambdaw:w["top"])text="\n".join([w["text"]forwinword_list])result[(row,col)]=textreturnresult

5.2 合并单元格检测与填充

defdetect_and_fill_merged_cells(table_data):""" 检测合并单元格并填充 """# 向下填充(列合并)forcolinrange(len(table_data[0])):last_value=Noneforrowinrange(len(table_data)):iftable_data[row][col]:last_value=table_data[row][col]eliflast_value:# 当前为空且上方有值 → 可能是合并单元格table_data[row][col]=last_value# 向右填充(行合并)—— 视业务需要# ...returntable_data

5.3 跨页连续性检测

defis_continuation_page(current_page,next_page):""" 判断下一页是否是当前表格的续页 """# 方法1:检测序号连续性current_last_seq=get_last_sequence(current_page)next_first_seq=get_first_sequence(next_page)ifnext_first_seqandcurrent_last_seq:returnnext_first_seq==current_last_seq+1# 方法2:检测"续"关键词next_header=extract_header(next_page)if"续"innext_headeror"接上页"innext_header:returnTrue# 方法3:列数一致且格式相似returnlen(current_page[0])==len(next_page[0])

六、校验清单

处理复杂场景后必须检查:

校验项方法失败处理
序号连续性set(range(1, n+1)) - set(实际序号)标记缺失行,人工复核
城市名不重复(同一行)同一记录中城市名只出现一次检测合并单元格是否未填充
列数一致性所有记录列数相同标记异常行,检查多行内容拆分
跨页后记录数总记录数 = 各页之和 - 表头行数检查表头去重逻辑
合并单元格填充随机抽样检查填充值是否正确标记异常,人工复核

七、一句话总结

PDF 表格解析的本质是"视觉还原工程":合并单元格是"空值填充",多行内容是"同格拼接",跨页是"连续性拼接",表头是"重复去重"。没有魔法,只有对坐标、空白、文字的精细工程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询