使用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
相关产品推荐
相关产品推荐

