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

使用pdfplumber提取PDF表格行格式不一致的问题求助

解决PDF表格提取后的规整化与数据清洗问题

问题背景

尝试提取塞浦路斯2023年6月30日航空器注册PDF,使用pdfplumber完成初步提取后,遇到以下格式问题:

  • S/N与REG MARKS列出现内容合并
  • AIRCRAFT OWNER / OPERATOR列的条目(如5B-DDH、5B-DDR)被拆分为多行
  • 多行单元格内容被错误拆分

原提取脚本如下:

import pandas as pd
import pdfplumber
import numpy as np

pdf_file = 'C:/Users/xxx/Downloads/AIRCRAFT REGISTER  30 JUN 2023 public.pdf'
all_tables = []

with pdfplumber.open(pdf_file) as pdf:
    for page in pdf.pages:
        cropped_page = page.crop(bbox=(0, 0, 825, 535))

        table_settings = {
            "vertical_strategy": "text",
            "horizontal_strategy": "text",
        }

        table = cropped_page.extract_table(table_settings=table_settings)

        if table:
            for row in table:
                if row[0] or not all_tables:
                    all_tables.append(row)
                else:
                    all_tables[-1] = [f"{a} {b}".strip() for a, b in zip(all_tables[-1], row)]

df_tables = pd.DataFrame(all_tables)

df_tables.replace([None, np.nan], '', inplace=True)

df_tables.reset_index(drop=True, inplace=True)

print(df_tables.head())

csv_file = 'C:/Users/xxx/Downloads/Cyprus_register_cleaned.csv'  
df_tables.to_csv(csv_file, index=False, encoding='utf-8')

数据清洗策略与修改方案

1. 优化pdfplumber的表格识别参数

原脚本使用text策略识别行列,容易因文本位置偏差导致列合并。改为结合lines与text的混合策略,利用PDF中的线条辅助定位列边界:

import re

table_settings = {
    "vertical_strategy": "lines",  # 优先使用垂直线条划分列
    "horizontal_strategy": "text",
    "explicit_vertical_lines": [50, 120, 200, 300, 450, 600, 750],  # 根据PDF实际列位置调整坐标
    "snap_tolerance": 3,  # 允许文本与线条的微小偏移
}

注:explicit_vertical_lines的坐标可通过pdfplumber可视化工具确认:调用page.draw_rects(page.extract_table(table_settings))查看列边界,调整数值匹配S/N、REG MARKS等列的实际位置。

2. 精准处理跨行单元格(针对OWNER/OPERATOR列)

原脚本的跨行合并逻辑过于宽泛,会误合并正常行。改为基于REG MARKS列的非空判断识别跨行:

with pdfplumber.open(pdf_file) as pdf:
    for page in pdf.pages:
        cropped_page = page.crop(bbox=(0, 0, 825, 535))
        table = cropped_page.extract_table(table_settings=table_settings)
        if not table:
            continue
        current_row = None
        for row in table:
            # 假设REG MARKS是第1列(索引从0开始),非空则为新行
            if row[1].strip():
                if current_row is not None:
                    all_tables.append(current_row)
                current_row = row.copy()
            else:
                # 仅合并OWNER/OPERATOR列(假设为第4列)
                current_row[4] = f"{current_row[4]} {row[4]}".strip()
        # 处理页面最后一行
        if current_row is not None:
            all_tables.append(current_row)

3. 修复列合并问题(S/N与REG MARKS)

若优化参数后仍有列合并,通过正则匹配拆分内容:

df_tables = pd.DataFrame(all_tables)

# 拆分S/N与REG MARKS合并列(假设合并列是第0列)
def split_sn_reg(col_value):
    reg_pattern = r"5B-[A-Z0-9]{3}"
    match = re.search(reg_pattern, col_value)
    if match:
        reg_mark = match.group()
        sn = col_value.replace(reg_mark, "").strip()
        return sn, reg_mark
    return col_value, ""

df_tables[['S/N', 'REG MARKS']] = df_tables[0].apply(lambda x: pd.Series(split_sn_reg(x)))
# 删除原合并列,调整列顺序
df_tables = df_tables.drop(columns=[0]).reindex(columns=['S/N', 'REG MARKS', 2, 3, 4, 5, 6])

4. 最终数据规整

# 替换空值,重命名列匹配原PDF
df_tables.replace(['', None, np.nan], '', inplace=True)
df_tables.columns = ['S/N', 'REG MARKS', 'TYPE SERIES', 'MODEL', 'AIRCRAFT OWNER / OPERATOR', 'ADDRESS', 'REMARKS']
df_tables.reset_index(drop=True, inplace=True)

df_tables.to_csv(csv_file, index=False, encoding='utf-8')

验证方法

提取后可通过以下方式确认准确性:

  • 核对5B-DDH、5B-DDR等条目,确认OWNER/OPERATOR列是否合并为单行
  • 检查S/N与REG MARKS列是否无交叉内容
  • 随机抽取10行数据与原PDF格式对比

内容的提问来源于stack exchange,提问作者Mark k

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 03:05:06