如何将PDF解析提取的信息整理为指定列的Pandas DataFrame?
作为Python新手,我尝试抓取PDF信息并构建DataFrame,已经用tika提取PDF内容得到一个列表,代码如下:
import pandas as pd import tika from tika import parser tika.initVM() file_name = "the_pdf_im_scraping" def input_file_processing(file_name): parsedPDF = parser.from_file(file_name) content = parsedPDF['content'] contentlist = content.split('\n') contentlist = list(filter(lambda a: a != '', contentlist)) return contentlist contentlist = input_file_processing(file_name)
提取得到的列表内容:
["John, Doe", '(803) 470-9419', "Company 1", 'PO Box 23425',
'Columbia, SC 14550', 'jmony@gmail.com', 'Aaron, Rust. ', 'Fax (864) 751-5784', '1317 Waterfall, #2334', 'Orlando, FL 32804',
'amail@gmail.com', 'Betn, S. Raul', 'Fax (864) 666-4484', 'S. Raul Baron, P.A.', '1456 Edgewater Drive, #2034', 'Orlando, FL 32804',
'georgeamon@gmail.com', 'Abueno, Daniel George', '(444) 123-2633',
'3456 Robert Drive', '#2316', 'Charleston, SC 29492',
'oldmail@outlook.com']
我需要把这些数据整理成包含last_name、first_name、company、phone_number、fax_number、address、city、state、zip_code、email列的DataFrame,但遇到了问题:部分条目没有公司名称或传真号码,还有部分地址被拆分成了列表中的多个元素,现在卡住了。
解决方案
步骤1:分析数据规律
观察列表能发现,每个联系人的信息是从姓名(格式为Last, First)开始,到邮箱(带@)结束的连续条目。我们可以先把整个列表按联系人分组,再逐个解析每组内容。
步骤2:用正则匹配各类字段
先定义正则表达式来识别不同类型的字段:
- 姓名:匹配
xxx, xxx格式 - 电话:匹配
(xxx) xxx-xxxx格式 - 传真:匹配以
Fax开头的行 - 邮箱:匹配带@的字符串
- 城市/州/邮编:匹配
City, State Zip格式
步骤3:分组并解析每个联系人
遍历列表,把每个联系人的信息单独分组,然后逐个提取字段,处理地址拼接、字段拆分。
完整代码实现
import pandas as pd import re # 定义正则表达式 name_pattern = re.compile(r'^[\w\s]+, [\w\s.]+$') # 匹配Last, First格式 phone_pattern = re.compile(r'^\(\d{3}\) \d{3}-\d{4}$') # 匹配(xxx) xxx-xxxx fax_pattern = re.compile(r'^Fax \(\d{3}\) \d{3}-\d{4}$') # 匹配Fax开头的传真 email_pattern = re.compile(r'^[\w.-]+@[\w.-]+$') # 匹配邮箱 city_state_zip_pattern = re.compile(r'^([\w\s]+), ([A-Z]{2}) (\d{5})$') # 匹配City, State Zip # 把contentlist按联系人分组 contacts = [] current_contact = [] for item in contentlist: item = item.strip() # 去除首尾空格 # 遇到姓名且当前组不为空时,先把之前的组存入contacts if name_pattern.match(item) and current_contact: contacts.append(current_contact) current_contact = [item] elif name_pattern.match(item): current_contact = [item] else: current_contact.append(item) # 加入最后一个联系人 if current_contact: contacts.append(current_contact) # 解析每个联系人组,生成结构化数据 structured_data = [] for contact in contacts: data = { 'last_name': '', 'first_name': '', 'company': '', 'phone_number': '', 'fax_number': '', 'address': '', 'city': '', 'state': '', 'zip_code': '', 'email': '' } # 提取姓名 name = contact[0].split(',') data['last_name'] = name[0].strip() data['first_name'] = name[1].strip().replace('.', '') # 去掉末尾的点 # 遍历剩余元素 address_lines = [] for item in contact[1:]: if phone_pattern.match(item): data['phone_number'] = item elif fax_pattern.match(item): data['fax_number'] = item.replace('Fax ', '') # 去掉Fax前缀 elif email_pattern.match(item): data['email'] = item elif city_state_zip_pattern.match(item): # 拆分城市、州、邮编 match = city_state_zip_pattern.match(item) data['city'] = match.group(1) data['state'] = match.group(2) data['zip_code'] = match.group(3) else: # 剩下的就是公司或地址行,先判断是否是公司(如果还没填公司的话) if not data['company']: # 假设公司名称不是数字开头,就认为是公司(可根据实际调整) if not item[0].isdigit(): data['company'] = item else: address_lines.append(item) else: address_lines.append(item) # 拼接地址行 data['address'] = ', '.join(address_lines) structured_data.append(data) # 转成DataFrame df = pd.DataFrame(structured_data) print(df)
代码说明
- 分组逻辑:通过识别姓名开头和邮箱结尾,把每个联系人的信息单独分组。
- 字段匹配:用正则精准识别各类字段,避免误判。
- 地址处理:把分散的地址行拼接成完整地址,同时拆分城市、州、邮编。
- 兼容缺失字段:如果某个字段不存在(比如没有公司或传真),会保留空值,不影响DataFrame构建。
内容的提问来源于stack exchange,提问作者Peter Languilla

