如何合并pandas DataFrame中同属一句的PDF表格拆分行
Camelot提取PDF表格缩进拆分行通用合并方案
核心判断逻辑
Camelot因PDF缩进拆分的错行有两个可通用识别的特征,不需要硬编码行号适配:
- 被拆分到下一行的内容,对应表格中原本每行都应填充值的核心列(比如序号、分类、金额列)必然全为空
- 新起的正常行,要么核心列有值,要么行首是规范的项目符号标记;被拆分的续行要么无项目符号,要么项目符号错位到核心列为空的行
可直接复用的代码实现
import pandas as pd import re # -------------------------- 配置项:按需修改即可 -------------------------- TEXT_CONTENT_COL = "item_content" # 存储项目符号文本的列名 NON_EMPTY_KEY_COLS = ["row_id", "item_type"] # 正常行必然有值的核心列,空值即判定为拆分行 # 匹配常见项目符号开头:小写字母+括号、罗马数字+括号、短横/圆点列表标记 BULLET_REGEX = re.compile(r'^[a-z]\)|^[ivx]+\)|^[IVX]+\)|^[-*•]') # ------------------------------------------------------------------------ def merge_camelot_split_rows(input_df: pd.DataFrame) -> pd.DataFrame: df = input_df.reset_index(drop=True).copy() drop_row_ids = [] # 第一轮合并:拼接无项目符号的纯续行 for row_idx in range(1, len(df)): current_row = df.iloc[row_idx] # 判定当前行是否为拆分行:核心列全空 + 不是新的项目符号开头 key_cols_all_empty = all( pd.isna(current_row[col]) or str(current_row[col]).strip() == "" for col in NON_EMPTY_KEY_COLS ) is_new_bullet_line = bool(BULLET_REGEX.match(str(current_row[TEXT_CONTENT_COL]).strip())) if key_cols_all_empty and not is_new_bullet_line: # 文本追加到上一行,标记当前行为待删除 prev_text = str(df.iloc[row_idx-1][TEXT_CONTENT_COL]).strip() curr_text = str(current_row[TEXT_CONTENT_COL]).strip() df.iloc[row_idx-1, df.columns.get_loc(TEXT_CONTENT_COL)] = f"{prev_text} {curr_text}" drop_row_ids.append(row_idx) # 清理已合并的续行 df = df.drop(index=drop_row_ids).reset_index(drop=True) drop_row_ids.clear() # 第二轮合并:拼接错位到下一行的项目符号行 for row_idx in range(1, len(df)): current_row = df.iloc[row_idx] prev_text = str(df.iloc[row_idx-1][TEXT_CONTENT_COL]).strip() curr_text = str(current_row[TEXT_CONTENT_COL]).strip() key_cols_all_empty = all( pd.isna(current_row[col]) or str(current_row[col]).strip() == "" for col in NON_EMPTY_KEY_COLS ) is_curr_line_bullet = bool(BULLET_REGEX.match(curr_text)) # 上一行末尾无闭合标点(句号、分号、感叹号、问号)+ 当前行是项目符号+核心列空,判定为拆分错位 prev_line_unclosed = not re.search(r'[。;;.!?]$', prev_text) if key_cols_all_empty and is_curr_line_bullet and prev_line_unclosed: df.iloc[row_idx-1, df.columns.get_loc(TEXT_CONTENT_COL)] = f"{prev_text}\n{curr_text}" drop_row_ids.append(row_idx) return df.drop(index=drop_row_ids).reset_index(drop=True)
使用示例
import camelot # 提取PDF表格,flavor参数根据你的表格类型选lattice/stream camelot_tables = camelot.read_pdf("target.pdf", pages="1-end", flavor="stream") raw_df = camelot_tables[0].df # 按提取结果给列重命名,和上方配置项保持一致 raw_df.columns = ["row_id", "item_type", "item_content"] # 执行行合并 cleaned_df = merge_camelot_split_rows(raw_df)
适配提示:如果你的文档用了特殊项目符号格式,只需要修改
BULLET_REGEX的正则规则;如果核心列有差异,调整NON_EMPTY_KEY_COLS配置即可覆盖绝大多数同类拆分场景,无需针对单个文件写定制逻辑。
内容的提问来源于stack exchange,提问作者Parth chokhra
相关产品推荐
相关产品推荐

