使用Python有效識別和合并PDF的跨頁表格
前言
在處理大量包含表格數(shù)據(jù)的PDF文檔時,一個常見的挑戰(zhàn)是這些表格可能跨越多頁。手動合并這些表格不僅耗時,而且容易出錯。幸運的是,通過使用Python和一些強大的庫,我們可以自動化這一過程,有效地識別和合并跨頁表格。
1.所需庫與安裝
為了實現(xiàn)這個功能,我們將使用以下Python庫:
- pandas:用于數(shù)據(jù)處理和分析。
- camelot:用于從PDF文檔中提取表格。
- pdfplumber:用于讀取PDF文檔中的文本和布局信息。
- win32com.client:用于將Word文檔轉(zhuǎn)換為PDF(僅限Windows環(huán)境)。
確保你的環(huán)境中安裝了這些庫。可以通過運行以下命令來安裝它們:
pip install pandas camelot-py pdfplumber pywin32
import pandas as pd import camelot # 基于文本的 PDF,而不適用于掃描文檔 import os from win32com.client import Dispatch from os import walk import pdfplumber # 文件夾路徑 path = 'E:\\data\\table_json-master' # 閾值,判斷表格是否位于頁面頂部或底部 topthreshold = 0.2 dthreshold = 0.8 # 將word文件轉(zhuǎn)化為PDF wdFormatPDF = 17
步驟詳解
- 識別表格邊界
利用pdfplumber,我們可以檢測每一頁上的表格邊界,這有助于判斷表格是否位于頁面頂部或底部,以及是否跨頁。 - 表格識別與跨頁檢查
camelot庫被用來識別PDF文檔中的表格。我們檢查每一表格是否在頁面的頂部或底部結(jié)束,以此判斷它是否可能是一個跨頁表格。 - 跨頁表格合并
如果確定表格跨頁并且列數(shù)匹配,我們使用camelot再次讀取這些頁面并將表格數(shù)據(jù)合并到一個DataFrame中,然后將結(jié)果保存為CSV或JSON文件。
2.提供 doc2pdf 函數(shù)用于將 Microsoft Word 文檔轉(zhuǎn)換為 PDF 格式
將文檔從 Word 轉(zhuǎn)換為 PDF 的方法非常適合批量轉(zhuǎn)換,即當(dāng)你有多個需要自動轉(zhuǎn)換的 Word 文檔時。但是請注意,根據(jù)文檔的數(shù)量和大小,這個操作可能會比較慢,因為每次轉(zhuǎn)換都涉及到打開 Word,加載文檔,將其保存為 PDF,然后再關(guān)閉文檔和 Word 應(yīng)用程序。
def doc2pdf(input_file):
word = Dispatch('Word.Application')
doc = word.Documents.Open(input_file)
doc.SaveAs(input_file.replace(".docx", ".pdf"), FileFormat=wdFormatPDF)
doc.Close()
word.Quit()
# 遍歷文件夾,將其中的word文件轉(zhuǎn)化為PDF
for root, dirs, filenames in walk(path):
for file in filenames:
if file.endswith(".doc") or file.endswith(".docx"):
doc2pdf(os.path.join(root, file))
win32com.client 庫是針對 Windows 環(huán)境的,依賴于機器上安裝的 Microsoft Word。
3.定義函數(shù),用于識別PDF文檔中表格的位置,特別是跨頁表格
- get_table_bounds(page):
這個函數(shù)接收一個PDF頁面對象,使用pdfplumber庫的find_tables()方法找到頁面上的所有表格,并返回一個包含每個表格邊界坐標(biāo)的列表。每個表格的邊界由一個四元組表示,包含左、頂、右、底的坐標(biāo)。 - is_first_table_at_top_of_page(page):
這個函數(shù)接收一個PDF頁面對象,計算頁面高度,然后獲取第一個表格的邊界,判斷這個表格的頂部位置是否低于頁面頂部的某個閾值(topthreshold)。如果低于這個閾值,意味著表格可能從頁面的頂部開始,返回一個字典,指示這個條件是否滿足。 - is_last_table_at_bottom_of_page(page):
類似于is_first_table_at_top_of_page,但檢查的是最后一個表格的底部位置是否高于頁面底部的某個閾值(dthreshold),以判斷表格是否接近頁面底部。 - is_table_ending_on_page(pdf_path, page, npage):
這個函數(shù)接收PDF文件路徑、pdfplumber的頁面對象和頁碼,使用camelot庫讀取下一頁的表格。如果存在表格,它檢查最后一個表格是否接近當(dāng)前頁面的底部,并返回一個布爾值和最后一行的列數(shù)。 - is_table_starting_on_page(pdf_path, page, npage):
與is_table_ending_on_page類似,但檢查的是下一個頁面的第一個表格是否從頁面頂部開始,并返回一個布爾值和第一行的列數(shù)。
def is_first_table_at_top_of_page(page):
page_height = page.height
table_bounds_list = get_table_bounds(page)
if table_bounds_list:
first_table_bounds = table_bounds_list[0]
is_first_table_at_top = first_table_bounds[1] < topthreshold * page_height
return {
"is_first_table_at_top": is_first_table_at_top,
}
else:
return {
"is_first_table_at_top": False,
}
def is_last_table_at_bottom_of_page(page):
page_height = page.height
table_bounds_list = get_table_bounds(page)
if table_bounds_list:
last_table_bounds = table_bounds_list[-1]
is_last_table_at_bottom = last_table_bounds[3] > dthreshold * page_height
return {
"is_last_table_at_bottom": is_last_table_at_bottom,
}
else:
return {
"is_last_table_at_bottom": False,
}
def get_table_bounds(page):
table_bounds_list = []
tables = page.find_tables()
for table in tables:
x0, top, x1, bottom = table.bbox
table_bounds_list.append((x0, top, x1, bottom))
return table_bounds_list
def is_table_ending_on_page(pdf_path, page, npage):
tables = camelot.read_pdf(pdf_path, pages=str(npage + 1))
if not tables:
return False, 0
table = tables[-1]
last_row = table.df.iloc[-1]
last_table_result = is_last_table_at_bottom_of_page(page)
return last_table_result['is_last_table_at_bottom'], len(last_row)
def is_table_starting_on_page(pdf_path, page, npage):
tables = camelot.read_pdf(pdf_path, pages=str(npage + 1))
if not tables:
return False, 0
table = tables[0]
first_row = table.df.iloc[0]
first_table_result = is_first_table_at_top_of_page(page)
return first_table_result['is_first_table_at_top'], len(first_row)
4.識別PDF文件中跨頁的表格并將其合并
- 獲取PDF文件列表:
使用列表推導(dǎo)式和os.listdir(path)函數(shù),篩選出所有以.pdf結(jié)尾的文件,形成一個包含所有PDF文件名的列表。 - 遍歷PDF文件: 對于每一個PDF文件,首先打印一條信息表明正在處理哪個文件。
- 構(gòu)造完整的PDF文件路徑。
使用camelot.read_pdf函數(shù)讀取整個PDF文檔中的所有表格。 - 檢查跨頁表格: 使用pdfplumber庫打開PDF文件,遍歷除了最后一頁的所有頁面。
對于每一頁,使用之前定義的函數(shù)is_table_ending_on_page和is_table_starting_on_page來檢查表格是否在該頁結(jié)束并在下一頁開始。 如果發(fā)現(xiàn)表格跨頁,并且連續(xù)兩頁的表格具有相同的列數(shù)(意味著它們可能是同一表格的一部分),則記錄這對頁面號。 - 合并跨頁表格: 如果找到了跨頁的表格,代碼會嘗試合并這些表格。 使用一個列表current_merge來跟蹤需要合并的頁面范圍。
當(dāng)遇到新的跨頁表格序列時,如果前一個序列尚未合并,先執(zhí)行合并操作。
# 獲取文件夾中所有PDF文件的列表
pdf_files = [f for f in os.listdir(path) if f.endswith('.pdf')]
print(pdf_files)
# 遍歷每個PDF文件并使用Camelot讀取
for pdf_file in pdf_files:
cross_page_tables = []
print(f'開始對{pdf_file}進行表格識別')
pdf_path = os.path.join(path, pdf_file)
tables = camelot.read_pdf(pdf_path, pages='all')
# 導(dǎo)出pdf所有的表格為json文件
tables.export(f'{pdf_file}.json', f='json') # json, excel, html, sqlite, markdown
with pdfplumber.open(pdf_path) as pdf:
for i, page in enumerate(pdf.pages[:-1]):
table_ending, ending_col_count = is_table_ending_on_page(pdf_path, page, i)
if i+ 1 < len(pdf.pages):
table_starting, starting_col_count = is_table_starting_on_page(pdf_path, page, i + 1)
if table_ending and table_starting and ending_col_count == starting_col_count:
cross_page_tables.append((i, i + 1))
print(f" {pdf_file}的表格在第 {i + 1} 頁和第 {i + 2} 頁之間跨頁,并且最后一行和下一頁的第一行列數(shù)相同")
# 處理所有跨頁表格
if cross_page_tables:
current_merge = []
for (start_page, end_page) in cross_page_tables:
if not current_merge or start_page == current_merge[-1]:
current_merge.append(end_page)
else:
# 合并當(dāng)前跨頁表格
merged_table = camelot.read_pdf(pdf_path, pages=",".join(map(str, [p + 1 for p in current_merge])))
full_df = pd.concat([table.df for table in merged_table])
full_df.to_csv(f'{pdf_file}_merged_{"_".join(map(str, [p + 1 for p in current_merge]))}.csv',
index=False)
full_df.to_json(f'{pdf_file}_merged_{"_".join(map(str, [p + 1 for p in current_merge]))}.json',
orient='records')
print(f'合并表格已保存為 {pdf_file}_merged_{"_".join(map(str, [p + 1 for p in current_merge]))}.csv 和 {pdf_file}_merged_{"_".join(map(str, [p + 1 for p in current_merge]))}.json')
# 重置合并列表
current_merge = [start_page, end_page]
5.總結(jié)
通過上述步驟,可以自動化地處理大量的PDF文檔,識別和合并跨頁表格,大大提高了數(shù)據(jù)處理的效率和準(zhǔn)確性。這種方法特別適用于需要頻繁分析大量文檔的工作場景。
到此這篇關(guān)于使用Python有效識別和合并PDF的跨頁表格的文章就介紹到這了,更多相關(guān)Python識別和合并PDF跨頁表格內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python?的矩陣傳播機制Broadcasting和矩陣運算
這篇文章主要介紹了Python?的矩陣傳播機制Broadcasting和矩陣運算,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-06-06
Python結(jié)合多線程與協(xié)程實現(xiàn)高效異步請求處理
在現(xiàn)代Web開發(fā)和數(shù)據(jù)處理中,高效處理HTTP請求是關(guān)鍵挑戰(zhàn)之一,本文將結(jié)合Python異步IO(asyncio)和多線程技術(shù),探討如何優(yōu)化請求處理邏輯,解決常見的線程事件循環(huán)問題,有需要的小伙伴可以根據(jù)需求進行選擇2025-04-04
python+mysql實現(xiàn)學(xué)生信息查詢系統(tǒng)
這篇文章主要為大家詳細介紹了python+mysql實現(xiàn)學(xué)生信息查詢系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02
Python利用Diagrams繪制漂亮的系統(tǒng)架構(gòu)圖
Diagrams 是一個基于Python繪制云系統(tǒng)架構(gòu)的模塊,它能夠通過非常簡單的描述就能可視化架構(gòu)。本文將利用它繪制漂亮的系統(tǒng)架構(gòu)圖,感興趣的可以了解一下2023-01-01

