You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合并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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:30:52