将PDF多列名单转换为DataFrame的Python代码报错求助
问题描述
需要处理PDF第13-15页的名单:其中第13、14页为3列式布局,第15页为2列式布局,最终要整理成带有指定列名的DataFrame。
第13页第一列的示例内容如下:
AUVERGNE-RHÔNE-ALPES 24 01. AIN | 1 | Malafretaz Plage principale 03. ALLIER | 1 | Vieure Plage du plan d’eau de Vieure 07. ARDÈCHE| 1 |Saint-Martin-d’Ardèche Plage du Grain de sel 15. CANTAL | 1 | Trémouille Lastioulles
理想的CSV输出格式为:
region,region_code,dpt_code,departement,flag,commune,plage AUVERGNE-RHÔNE-ALPES,24,01.,AIN,| 1 |,Malafretaz,Plage principale , ,03.,ALLIER,| 1 |,Vieure,Plage du plan d’eau de Vieure , ,07.,ARDÈCHE,| 1 |,Saint-Martin-d’Ardèche,Plage du Grain de sel , ,15.,CANTAL,| 1 |,Trémouille,Lastioulles
尝试了以下Python代码,但在列整理步骤中出错,寻求解决方法:
import tabula import pandas as pd # Link to PDF pdf_url = "https://www.teragir.org/wp-content/uploads/2023/05/DP-PAVBLEU-2023-WEB.pdf" # Use Tabula to extrat the data on page 13 (I start with one single page) df = tabula.read_pdf(pdf_url, pages=13)[0] # Delete empty lines df.dropna(inplace=True) # Organize the columns df.columns = ["Region", "Region_N", "Dpt_N", "Departement", "Flag", "Commune", "Plage"] # Fill the missing values in the columns "Region" et "Region_N" df["Region"].fillna(method="ffill", inplace=True) df["Region_N"].fillna(method="ffill", inplace=True) # Save the table in a CSV df.to_csv("liste_plages.csv", index=False)
解决方案
问题核心在于Tabula默认提取的列结构不符合预期,且存在跨行文本拆分(比如“Plage du plan d’eau de Vieure”分两行显示),需要调整提取策略并手动处理文本分割。
完整解决代码
import tabula import pandas as pd import re pdf_url = "https://www.teragir.org/wp-content/uploads/2023/05/DP-PAVBLEU-2023-WEB.pdf" def process_page(page_num, is_three_cols=True): # 根据页面列布局定义分隔位置(可根据实际PDF微调) columns = [100, 300, 500, 700] if is_three_cols else [100, 400] # 以流式模式提取页面数据,准确定位列分隔 df = tabula.read_pdf( pdf_url, pages=page_num, stream=True, columns=columns, pandas_options={'header': None} )[0] # 合并所有列的文本,处理跨行拆分的内容 df['combined'] = df.apply( lambda row: ' '.join([str(cell) for cell in row if pd.notna(cell)]), axis=1 ) df = df[['combined']].dropna() result = [] current_region = None current_region_code = None for line in df['combined']: line = line.strip() if not line: continue # 匹配区域名称(纯字母/空格/连字符组成) if re.match(r'^[A-ZÀ-ÿ\s-]+$', line): current_region = line continue # 匹配区域代码(纯数字) elif line.isdigit(): current_region_code = line continue # 处理数据行(包含|分隔符) elif '|' in line: # 提取flag部分 flag_part = re.search(r'\|\s*\d+\s\|', line).group() left_segment = line.split(flag_part)[0].strip() right_segment = line.split(flag_part)[1].strip() # 拆分省份代码和名称 dpt_match = re.match(r'^(\d+\.)\s*(.*)', left_segment) dpt_code = dpt_match.group(1) if dpt_match else '' departement = dpt_match.group(2).strip() if dpt_match else left_segment # 拆分市镇和沙滩名称 parts = right_segment.split(' ', 1) commune = parts[0].strip() plage = parts[1].strip() if len(parts) >=2 else '' result.append({ 'region': current_region, 'region_code': current_region_code, 'dpt_code': dpt_code, 'departement': departement, 'flag': flag_part, 'commune': commune, 'plage': plage }) return pd.DataFrame(result) # 分别处理3列布局的13、14页和2列布局的15页 df_13 = process_page(13) df_14 = process_page(14) df_15 = process_page(15, is_three_cols=False) # 合并所有页面数据并填充缺失的区域信息 final_df = pd.concat([df_13, df_14, df_15], ignore_index=True) final_df['region'] = final_df['region'].ffill() final_df['region_code'] = final_df['region_code'].ffill() # 保存为CSV final_df.to_csv("liste_plages.csv", index=False)
代码说明
process_page函数:根据页面列数调整提取参数,合并跨行文本,通过正则匹配拆分出目标字段。- 跨行文本处理:将拆分到下一行的沙滩名称合并回对应数据行,避免信息断裂。
- 正则匹配:精准识别区域名称、区域代码和数据行结构,确保各字段拆分准确。
- 向前填充:对后续行的区域名称和代码进行填充,保证数据完整性。
内容的提问来源于stack exchange,提问作者user11278201
相关产品推荐
相关产品推荐

