用python提取PDF表格内容保存到excel

一 提取pdf方法介绍

任务是用python提取PDF里的表格文件到excel里面去。作为一个 学了一个周python的人来讲固然像尝试一下看能不能作到,事实证实是能够的只是可能代码有点烂。。。。。。
样本大概是这样的
提取pdf表格样本
首先网上查一下用python处理pdf文件的方法,感受处理pdf文件的有好多种方法,各自有各自的特色,印象最深的是转成html文件的pdf2htmlEX,和提取文本的pdfminer,还有最后用的Tabula。分别介绍一下记录。html

  • pdf2htmlEX
    作这个的时候不怎么了解html,并且也有人曾经尝试过彷佛挺麻烦的,因此就没有去尝试。
  • pdfminer
    pdfminer功能很是的强大,能够转好多东西,用获得直接阅读官方文档,因为解析PDF是一件很是耗时和内存的工做,所以PDFMiner使用了一种称做lazy parsing的策略,只在须要的时候才去解析,以减小时间和内存的使用。
    解析结构图大概是这样的
    在这里插入图片描述
    PDFParser:从一个文件中获取数据
    PDFDocument:保存获取的数据,和PDFParser是相互关联的
    PDFPageInterpreter处理页面内容
    PDFDevice将其翻译成你须要的格式
    PDFResourceManager用于存储共享资源,如字体或图像。
    这个模块是经过页面布局解析出来的,解析出来大概以下
    在这里插入图片描述
    一句话归纳:把相应布局的内容保存到不一样的地方如上图布局,用到的什么就调用相应的函数读取。
    尝试过用这个方法提取表格可是挺麻烦的,网上别人给的思路是能够用识别线条的方法提取表格内容,若是能写出来应该效果挺不错的,时间有限加上网上也没用教程能够参考,都是不推荐用这个模块提取表格的,就没有继续尝试。提取文本内容应该很合适。
  • Tabula
    专门用来提取pdf里的表格的,支持导出csv.,excel文件
    提取出来一个页面若是有两个表格会放到一个list里,每一个表格一个元素。一个表格一个dataframe结构的文件,因此须要配合pandas模块使用。

二 练习项目介绍

很久以前写了,一些细节忘记了,不过当时注释的很清楚直接上代码,也是怕哪天电脑忽然死机代码没了。。。换个地儿存。。。这个模块提取的效率是真的慢。。。。。据说能够调Java的程序提取效率快三倍可是没学Java也就没有去了解,之后还用获得应该能够去了解一下。。。。。。python

# -*- coding: utf-8 -*- import os import gc from PyPDF2.pdf import PdfFileReader from tabula import read_pdf import pandas as pd from openpyxl import load_workbook, Workbook import datetime def data_process2(dataframe2): """ 三步: 删除只有一个非空或者全空的列 从第一列开始日后合并直到遇到只有第一列不为空或者全不为空则处理下一步 遇到只有第一行不为空则检查接下来的第三行若是同样状况则接下来三行合并成一行 """ #此循环处理只有一个非空或者全空列的状况,防止影响下面的处理 k = 0 while True: if dataframe2.notnull().sum(axis=0)[k] <= 1: print("%d空列\n", k, dataframe2.notnull()[k]) if k+1 == dataframe2.columns.size: dataframe2 = dataframe2.iloc[0:, :k] else: dataframe_left = dataframe2.iloc[0:, :k] dataframe_right = dataframe2.iloc[0:, k + 1:] dataframe2 = pd.concat([dataframe_left, dataframe_right], axis=1, ignore_index=True) k = k-1 if k >= dataframe2.columns.size-1: break k = k+1 i = 0 t = 0 print("去掉空列后\n", dataframe2) #空字符代替NaN防止NaN和其余合并时全为空 dataframe2_copy = dataframe2.fillna('', inplace=False) #此循环处理表头 while True: if i == 0: if dataframe2.notnull().sum(axis=1)[0] == dataframe2.columns.size: break if dataframe2.notnull().sum(axis=1)[0] == 1 and dataframe2.notnull().iat[0, 0]: break i = i + 1 else: if dataframe2.notnull().sum(axis=1)[i] == dataframe2.columns.size: t = t+1 break if dataframe2.notnull().sum(axis=1)[i] == 1 and dataframe2.notnull().iat[i, 0]: t = t+1 break dataframe2_copy.iloc[t] = dataframe2_copy.iloc[t] + dataframe2_copy.iloc[i] i = i+1 if i >= len(dataframe2): t = t + 1 break print("处理表头中\n", dataframe2_copy) #去掉空行,而且从新索引 dataframe2_copy.dropna(axis=0, how='all', inplace=True) dataframe2_copy = dataframe2_copy.reset_index(drop=True) #次循环处理表里的数据 while i < len(dataframe2): if i+2 >= len(dataframe2): for p in range(len(dataframe2)-i): dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i+p] t = t+1 break elif dataframe2.notnull().sum(axis=1)[i] == 1 and dataframe2.notnull().iat[i, 0]: if dataframe2.notnull().sum(axis=1)[i+2] == 1 and dataframe2.notnull().iat[i+2, 0]: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] + dataframe2_copy.iloc[i+1] + dataframe2_copy.iloc[i+2] i = i+3 elif i+4 < len(dataframe2): if dataframe2.notnull().sum(axis=1)[i + 1] == 1 and dataframe2.notnull().sum(axis=1)[i + 3] == 1 and dataframe2.notnull().sum(axis=1)[i + 4] == 1 and dataframe2.notnull().iat[i+1, 0] and dataframe2.notnull().iat[i+3, 0] and dataframe2.notnull().iat[i+4, 0]: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] + dataframe2_copy.iloc[i + 1] + dataframe2_copy.iloc[i + 2] + dataframe2_copy.iloc[i + 3] + dataframe2_copy.iloc[i + 4] i = i + 5 else: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] i = i + 1 else: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] i = i + 1 else: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] i = i+1 t = t+1 print("一个表的数据\n",dataframe2_copy) return dataframe2_copy.iloc[:t] def data_process1(dataframes): """ 根据两个空格拆分列数据合并 适用于数据均为str类型表格 若是非str型合并后为空数据丢失 """ dataframes.fillna('', inplace=True) print("处理前数据:\n", dataframes) n = 0 while True: try: dataframes[n].str.split(' ', expand=True)#一列全是非str pass dataframes[n] = dataframes[n].astype('str')#处理有一部分为非str状况,防止数据丢失 over_data = dataframes[n].str.split(' ', expand=True) over_data.fillna('', inplace=True) except: print("遇到非str型的列 pass") n = n+1 if n >= dataframes.columns.size: break else: continue print("重叠的列:\n", over_data) if n-1 < 0: dataframe_right = dataframes.iloc[0:, n + 1:] dataframes = pd.concat([over_data, dataframe_right], axis=1, ignore_index=True) elif n+1 > dataframes.columns.size: dataframe_left = dataframes.iloc[0:, :n] dataframes = pd.concat([dataframe_left, over_data], axis=1, ignore_index=True) else: dataframe_left = dataframes.iloc[0:, :n] dataframe_right = dataframes.iloc[0:, n+1:] dataframes = pd.concat([dataframe_left, over_data, dataframe_right], axis=1, ignore_index=True) n = n + over_data.columns.size if n >= dataframes.columns.size: break print("处理后数据\n:", dataframes) return dataframes def getCashflowAggregation(dataframe1): pass def pdf_to_xlsx(folder): """ 提取文件夹的PDF里表格数据 对数据作初步整理 对每一个dataframe识别提取想要的数据保存到相应的sheet里, 输出同名xlsx格式文件 """ files = os.listdir(folder) #遍历文件夹,找出PDF文件 pdfFile = [f for f in files if f.endswith(".pdf")] for pdfFiles in pdfFile: #创建一个和PDF同名的xlsx文件 pdfPath = os.path.join(folder, pdfFiles) xlsPath = pdfPath[:-3] + "xlsx" #创建Workbook而后和所要保存的数据表格链接,以后每次保存都会保存到不一样的Sheet中 Workbook(xlsPath) book = Workbook() book.save(filename=xlsPath) #获取PDF的页数 pdf = PdfFileReader(open(pdfPath, "rb")) page_counts = pdf.getNumPages() dataframe2 = pd.DataFrame() #遍历PDF每一页,提取出表格数据 for page in range(1, page_counts+1): try: pf = read_pdf(pdfPath, encoding='gbk', multiple_tables=True,pages = page) if len(pf) != 0: for t in range(len(pf)): dataframe1 = pf[t] dataframe1 = data_process2(dataframe1)#处理表头 dataframe1 = data_process1(dataframe1)#按空格拆分合并项 #CashflowAggregation = getCashflowAggregation(dataframe1) #列数相同的表格合并,而且删除重复项并保存 if dataframe2.empty: dataframe2 = dataframe1 elif dataframe1.columns.size == dataframe2.columns.size: dataframe2 = pd.concat([dataframe2,dataframe1],ignore_index=True) #删除重复项会影响池分布的匹配提取,可是能够很好的处理静动态池和现金流归集 #dataframe2.drop_duplicates(keep="first", inplace=True)#在原来的数据里删除重复项 print(dataframe2) else: print("列数:", dataframe1.columns.size) print(dataframe2) #保存在不一样的工做簿 writer = pd.ExcelWriter(xlsPath, engin='openpyxl') book = load_workbook(writer.path) writer.book = book dataframe2.to_excel(writer, sheet_name='shet') writer.close() dataframe2 = dataframe1 del(pf) gc.collect() except: gc.collect() print("Error Pass") continue #保存最后的数据表格到另外一个工做表里 writer = pd.ExcelWriter(xlsPath, engin='openpyxl') book = load_workbook(writer.path) writer.book = book dataframe2.to_excel(writer, sheet_name='shet') writer.close() star_time = datetime.datetime.now() pdf_to_xlsx("D:\\2018暑假\\新建文件夹") stop_time = datetime.datetime.now() print("程序运行时间:", stop_time-star_time) # -*- coding: utf-8 -*- import os import gc from PyPDF2.pdf import PdfFileReader from tabula import read_pdf import pandas as pd from openpyxl import load_workbook, Workbook import datetime def data_process2(dataframe2): """ 三步: 删除只有一个非空或者全空的列 从第一列开始日后合并直到遇到只有第一列不为空或者全不为空则处理下一步 遇到只有第一行不为空则检查接下来的第三行若是同样状况则接下来三行合并成一行 """ #此循环处理只有一个非空或者全空列的状况,防止影响下面的处理 k = 0 while True: if dataframe2.notnull().sum(axis=0)[k] <= 1: print("%d空列\n", k, dataframe2.notnull()[k]) if k+1 == dataframe2.columns.size: dataframe2 = dataframe2.iloc[0:, :k] else: dataframe_left = dataframe2.iloc[0:, :k] dataframe_right = dataframe2.iloc[0:, k + 1:] dataframe2 = pd.concat([dataframe_left, dataframe_right], axis=1, ignore_index=True) k = k-1 if k >= dataframe2.columns.size-1: break k = k+1 i = 0 t = 0 print("去掉空列后\n", dataframe2) #空字符代替NaN防止NaN和其余合并时全为空 dataframe2_copy = dataframe2.fillna('', inplace=False) #此循环处理表头 while True: if i == 0: if dataframe2.notnull().sum(axis=1)[0] == dataframe2.columns.size: break if dataframe2.notnull().sum(axis=1)[0] == 1 and dataframe2.notnull().iat[0, 0]: break i = i + 1 else: if dataframe2.notnull().sum(axis=1)[i] == dataframe2.columns.size: t = t+1 break if dataframe2.notnull().sum(axis=1)[i] == 1 and dataframe2.notnull().iat[i, 0]: t = t+1 break dataframe2_copy.iloc[t] = dataframe2_copy.iloc[t] + dataframe2_copy.iloc[i] i = i+1 if i >= len(dataframe2): t = t + 1 break print("处理表头中\n", dataframe2_copy) #去掉空行,而且从新索引 dataframe2_copy.dropna(axis=0, how='all', inplace=True) dataframe2_copy = dataframe2_copy.reset_index(drop=True) #次循环处理表里的数据 while i < len(dataframe2): if i+2 >= len(dataframe2): for p in range(len(dataframe2)-i): dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i+p] t = t+1 break elif dataframe2.notnull().sum(axis=1)[i] == 1 and dataframe2.notnull().iat[i, 0]: if dataframe2.notnull().sum(axis=1)[i+2] == 1 and dataframe2.notnull().iat[i+2, 0]: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] + dataframe2_copy.iloc[i+1] + dataframe2_copy.iloc[i+2] i = i+3 elif i+4 < len(dataframe2): if dataframe2.notnull().sum(axis=1)[i + 1] == 1 and dataframe2.notnull().sum(axis=1)[i + 3] == 1 and dataframe2.notnull().sum(axis=1)[i + 4] == 1 and dataframe2.notnull().iat[i+1, 0] and dataframe2.notnull().iat[i+3, 0] and dataframe2.notnull().iat[i+4, 0]: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] + dataframe2_copy.iloc[i + 1] + dataframe2_copy.iloc[i + 2] + dataframe2_copy.iloc[i + 3] + dataframe2_copy.iloc[i + 4] i = i + 5 else: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] i = i + 1 else: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] i = i + 1 else: dataframe2_copy.iloc[t] = dataframe2_copy.iloc[i] i = i+1 t = t+1 print("一个表的数据\n",dataframe2_copy) return dataframe2_copy.iloc[:t] def data_process1(dataframes): """ 根据两个空格拆分列数据合并 适用于数据均为str类型表格 若是非str型合并后为空数据丢失 """ dataframes.fillna('', inplace=True) print("处理前数据:\n", dataframes) n = 0 while True: try: dataframes[n].str.split(' ', expand=True)#一列全是非str pass dataframes[n] = dataframes[n].astype('str')#处理有一部分为非str状况,防止数据丢失 over_data = dataframes[n].str.split(' ', expand=True) over_data.fillna('', inplace=True) except: print("遇到非str型的列 pass") n = n+1 if n >= dataframes.columns.size: break else: continue print("重叠的列:\n", over_data) if n-1 < 0: dataframe_right = dataframes.iloc[0:, n + 1:] dataframes = pd.concat([over_data, dataframe_right], axis=1, ignore_index=True) elif n+1 > dataframes.columns.size: dataframe_left = dataframes.iloc[0:, :n] dataframes = pd.concat([dataframe_left, over_data], axis=1, ignore_index=True) else: dataframe_left = dataframes.iloc[0:, :n] dataframe_right = dataframes.iloc[0:, n+1:] dataframes = pd.concat([dataframe_left, over_data, dataframe_right], axis=1, ignore_index=True) n = n + over_data.columns.size if n >= dataframes.columns.size: break print("处理后数据\n:", dataframes) return dataframes def getCashflowAggregation(dataframe1): pass def pdf_to_xlsx(folder): """ 提取文件夹的PDF里表格数据 对数据作初步整理 对每一个dataframe识别提取想要的数据保存到相应的sheet里, 输出同名xlsx格式文件 """ files = os.listdir(folder) #遍历文件夹,找出PDF文件 pdfFile = [f for f in files if f.endswith(".pdf")] for pdfFiles in pdfFile: #创建一个和PDF同名的xlsx文件 pdfPath = os.path.join(folder, pdfFiles) xlsPath = pdfPath[:-3] + "xlsx" #创建Workbook而后和所要保存的数据表格链接,以后每次保存都会保存到不一样的Sheet中 Workbook(xlsPath) book = Workbook() book.save(filename=xlsPath) #获取PDF的页数 pdf = PdfFileReader(open(pdfPath, "rb")) page_counts = pdf.getNumPages() dataframe2 = pd.DataFrame() #遍历PDF每一页,提取出表格数据 for page in range(1, page_counts+1): try: pf = read_pdf(pdfPath, encoding='gbk', multiple_tables=True,pages = page) if len(pf) != 0: for t in range(len(pf)): dataframe1 = pf[t] dataframe1 = data_process2(dataframe1)#处理表头 dataframe1 = data_process1(dataframe1)#按空格拆分合并项 #CashflowAggregation = getCashflowAggregation(dataframe1) #列数相同的表格合并,而且删除重复项并保存 if dataframe2.empty: dataframe2 = dataframe1 elif dataframe1.columns.size == dataframe2.columns.size: dataframe2 = pd.concat([dataframe2,dataframe1],ignore_index=True) #删除重复项会影响池分布的匹配提取,可是能够很好的处理静动态池和现金流归集 #dataframe2.drop_duplicates(keep="first", inplace=True)#在原来的数据里删除重复项 print(dataframe2) else: print("列数:", dataframe1.columns.size) print(dataframe2) #保存在不一样的工做簿 writer = pd.ExcelWriter(xlsPath, engin='openpyxl') book = load_workbook(writer.path) writer.book = book dataframe2.to_excel(writer, sheet_name='shet') writer.close() dataframe2 = dataframe1 del(pf) gc.collect() except: gc.collect() print("Error Pass") continue #保存最后的数据表格到另外一个工做表里 writer = pd.ExcelWriter(xlsPath, engin='openpyxl') book = load_workbook(writer.path) writer.book = book dataframe2.to_excel(writer, sheet_name='shet') writer.close() star_time = datetime.datetime.now() pdf_to_xlsx("D:\\2018暑假\\新建文件夹") stop_time = datetime.datetime.now() print("程序运行时间:", stop_time-star_time)