从PDF文件获取表格中的数据,也是日常办公容易涉及到的一项工作。一个一个复制吧,效率确实太低了。用Python从PDF文档中提取表格数据,并写入Excel文件,灰常灰常高效。
上市公司的年报往往包含几百张表格,用它作为例子再合适不过,搞定这个,其他含表格的PDF都是小儿科了。今天以"保利地产年报"为例,这个PDF文档中有321页含有表格,总表格数超过这个数了。 先导入PDF读取模块pdfplumber
,随便挑一页看下表格数据的结构。如下,我们挑了第4页pages[3]
来读取其中的表格,并显示。这里读取表格,用到了extract_tables()
,即默认每页有多个表格。它会将单个表格的数据按行读取存入列表,再将每个表格的所有数据汇总存到一个上一级列表,最后将所有表格的数据汇总到一个大列表。而extract_table()
方法则只能读一张表,当一个页面有多张表,就默认选第一个,因此会漏掉后面的。而且它们的数据结构也不同,差异如下。
“保利地产年报”第四页如图所示,读取的结果存到列表table
,显示如下。
#观察读取出来的表格的数据结构
import pdfplumber
with pdfplumber.open("保利地产年报.pdf") as p:
page = p.pages[3] #选取第4页(起始页为0)
table = page.extract_tables() #多表格读取,存为嵌套列表
print(table)
[[['', '常用词语释义', None, None, None, None, None, ''], ['中国证监会', None, '', '指', '', '', '中国证券监督管理委员会', ''], ['国资委', None, '', '指', '', '', '国务院国有资产监督管理委员会', ''], ['上交所', None, '', '指', '', '上海证券交易所', None, None], ['公司、本公司、保利地产', None, '指', None, None, '保利发展控股集团股份有限公司,原名称保利房\n地产(集团)股份有限公司', None, None], ['报告期、本报告期', None, '', '指', '', '2018年度', None, None], ['元、万元、亿元', None, '', '指', '', '人民币元、人民币万元、人民币亿元', None, None]], [['公司的中文名称', '保利发展控股集团股份有限公司'], ['公司的中文简称', '保利地产'], ['公司的外文名称', 'Poly Developments and Holdings Group Co., Ltd.'], ['公司的外文名称缩写', 'PDH'], ['公司的法定代表人', '宋广菊']], [['', '董事会秘书', '证券事务代表'], ['姓名', '黄海', '尹超'], ['联系地址', '广东省广州市海珠区阅江中路688号保利国际广场北塔33层董事会办公室', None], ['电话', '020-89898833', None], ['传真', '020-89898666-8831', None], ['电子信箱', 'stock@polycn.com', None]], [['公司注册地址', '广州市海珠区阅江中路688号保利国际广场30-33层'], ['公司注册地址的邮政编码', '510308'], ['公司办公地址', '广州市海珠区阅江中路688号保利国际广场北塔30-33层'], ['公司办公地址的邮政编码', '510308'], ['公司网址', 'www.polycn.com;www.gzpoly.com'], ['电子信箱', 'stock@polycn.com']], [['公司选定的信息披露媒体名称', '《中国证券报》、《上海证券报》、《证券时报》'], ['登载年度报告的中国证监会指定网站的网址', 'www.sse.com.cn'], ['公司年度报告备置地点', '公司董事会办公室']]]
确保可正常读取表格,以及了解读取出来的表格的数据结构,下面就可以一次性读取出所有表格,并存入Excel文件中了。导入相应模块,然后使用pdfplumber
打开PDF文件。使用Workbook()
新建Excel工作簿,然后使用remove()
将其自带的工作表删除。因为我们想用PDF文件中表格所在的页码给相应的Excel工作表命名,以便二者的编号一致,方便后续查询。所以需要使用enumerate()
给PDF的页从1开始编号。然后使用extract_tables()
获取表格数据。 当然,如果当页没有表格,则extract_tables()
获得的是空值None
。在后续的操作中,空值会报错,所以加入if
语句来做个判断。只有当列表tables
不为空,即里面有货的时候,才建新的Excel表格,并执行后续的写入操作。列表tables
若为空(即当页没有表格),则直接跳到下一页。 当发现当页有表格后,新建一个Excel表,以“Sheet”加上此时PDF的页码(比如“Sheet3”)命名。在写入数据时,先用一个for
循环获得单个表格的数据,再用第二个for
循环获得表格中一行的数据,然后写入Excel表。最后保存数据。由于表格太多,程序运行时间较长,大约需要3分钟。
import pdfplumber
from openpyxl import Workbook
with pdfplumber.open("保利地产年报.pdf") as p:
wb = Workbook() #新建excel工作簿
wb.remove(wb.worksheets[0])#删除工作簿自带的工作表
for index,page in enumerate(p.pages,start = 1): #从1开始给所有页编号
tables = page.extract_tables() #读取表格
if tables: #判断是否存在表格,若不存在,则不执行下面的语句
ws = wb.create_sheet(f"Sheet{index}") #新建工作表,表名的编号与表在PDF中的页码一致
for table in tables: #遍历所有列表
for row in table: #遍历列表中的所有子列表,里面保存着行数据
ws.append(row) #写入excel表
wb.save("保利地产年报表格.xlsx")
数百个表格就这样潇洒地复制到Excel表格中了。
如果想要指定某个表格,在提取数据的时候指定页码即可。但如果想批量导出大量不同公司的年报的指定表格,则需要使用关键词定位了。还好,无论深圳市场还是上海市场,公司的年报中的标题基本都是唯一的,这给我们用标题做关键词提供了方便。假设我们需要提取公司“主要会计数据”下面的表格,则用关键词“主要会计数据”定位即可。如下以此为例进行操作。
import os
import pdfplumber
from openpyxl import Workbook
path='PDF' #文件所在文件夹
files = [path+"\\"+i for i in os.listdir(path)] #获取文件夹下的文件名,并拼接完整路径
key_words = "主要会计数据"
for file in files:
with pdfplumber.open(file) as p:
wb = Workbook() #新建excel工作簿
wb.remove(wb.worksheets[0])#删除工作簿自带的工作表
#获取关键词所在页及下一页的页码
pages_wanted = []
for index,page in enumerate(p.pages): #从0开始给所有页编号
if key_words in page.extract_text():
pages_wanted.append(index)
pages_wanted.append(index+1)
break
#提取指定页码里的表格
for i in pages_wanted:
page = p.pages[i]
tables = page.extract_tables() #读取表格
if tables: #判断是否存在表格,若不存在,则不执行下面的语句
ws = wb.create_sheet(f"Sheet{i+1}") #新建工作表,表名的编号与表在PDF中的页码一致
for table in tables: #遍历所有列表
for row in table: #遍历列表中的所有子列表,里面保存着行数据
ws.append(row) #写入excel表
wb.save("Excel\\{}.xlsx".format(file.split("\\")[1].split(".")[0]))
以上,增加了一段获取关键词所在页码及下一页的页码的程序。之所以要获取关键词下一页页码,是因为有些表格会跨页,为了不遗漏数据,宁愿多获取一点。一旦找到关键词所在页,马上用break
停止for
循环。后面再遍历pages_wanted
里面储存的页码,提取表格并写入Excel文件,并保存即可。批量获取的指定内容保存在Excel
文件夹下。
如果您有需要处理的问题,可发邮件到我邮箱:donyo@qq.com,一起探讨解决方案。微信公众号输入“源文件”提取所有源文件及资料。
最好的赞赏就是点亮下方“在看”,多给PythonOffice积攒一点人气哈!