1. 程式人生 > >用python提取PDF表格內容儲存到excel

用python提取PDF表格內容儲存到excel

一 提取pdf方法介紹

任務是用python提取PDF裡的表格檔案到excel裡面去。做為一個 學了一個周python的人來說當然像嘗試一下看能不能做到,事實證明是可以的只是可能程式碼有點爛。。。。。。 樣本大概是這樣的 提取pdf表格樣本 首先網上查一下用python處理pdf檔案的方法,感覺處理pdf檔案的有好多種方法,各自有各自的特點,印象最深的是轉成html檔案的pdf2htmlEX,和提取文字的pdfminer,還有最後用的Tabula。分別介紹一下記錄。

  • pdf2htmlEX 做這個的時候不怎麼了解html,而且也有人曾經嘗試過似乎挺麻煩的,所以就沒有去嘗試。
  • pdfminer pdfminer功能非常的強大,可以轉好多東西,用得到直接閱讀
    官方文件
    ,由於解析PDF是一件非常耗時和記憶體的工作,因此PDFMiner使用了一種稱作lazy parsing的策略,只在需要的時候才去解析,以減少時間和記憶體的使用。 解析結構圖大概是這樣的 在這裡插入圖片描述 PDFParser:從一個檔案中獲取資料 PDFDocument:儲存獲取的資料,和PDFParser是相互關聯的 PDFPageInterpreter處理頁面內容 PDFDevice將其翻譯成你需要的格式 PDFResourceManager用於儲存共享資源,如字型或影象。 這個模組是通過頁面佈局解析出來的,解析出來大概如下 在這裡插入圖片描述 一句話概括:把相應佈局的內容儲存到不同的地方如上圖佈局,用到的什麼就呼叫相應的函式讀取。 嘗試過用這個方法提取表格但是挺麻煩的,網上別人給的思路是可以用識別線條的方法提取表格內容,如果能寫出來應該效果挺不錯的,時間有限加上網上也沒用教程可以參考,都是不推薦用這個模組提取表格的,就沒有繼續嘗試。提取文字內容應該很合適。
  • Tabula 專門用來提取pdf裡的表格的,支援匯出csv.,excel檔案 提取出來一個頁面如果有兩個表格會放到一個list裡,每個表格一個元素。一個表格一個dataframe結構的檔案,所以需要配合pandas模組使用。

二 練習專案介紹

好久之前寫了,一些細節忘記了,不過當時註釋的很清楚直接上程式碼,也是怕哪天電腦突然宕機程式碼沒了。。。換個地兒存。。。這個模組提取的效率是真的慢。。。。。聽說可以調Java的程式提取效率快三倍但是沒學Java也就沒有去了解,以後還用得到應該可以去了解一下。。。。。。

# -*- 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)