? PDF解析是極其復雜的問題。不可能靠一個工具解決全部問題,尤其是五花八門,格式不統一的PDF文件。除非有鈔能力。如果沒有那就看看可以分為哪些問題。
? 提取文本內容,提取表格內容,提取圖片。我認為這些應該是分開做的事情。python有一些組件,是有專長的。
? 問題分解以后,最重要的一個事情是,版面分析。怎么確定邊界,就是哪一塊是什么內容?是正文,還是表格,還是圖片?
??文本、圖片及形狀涵蓋了常見的PDF元素,本文介紹利用
PyMuPDF
提取這些頁面元素,及其基本數據結構。本文會提供可運行的代碼!
一、技術選型?PyMuPDF
PyMuPDF
的Textpage
對象提供的extractDICT()
和extractRAWDICT()
用以獲取頁面中的所有文本和圖片(內容、位置、屬性),基本數據結構如下:
看到這里,有分類,有位置信息。
二、代碼演示
2.1 安裝
pip install PyMuPDF
2.2 demo代碼?
import fitz # PyMuPDFdef extract_text_blocks(pdf_path):# 打開 PDF 文件pdf_document = fitz.open(pdf_path)# 存儲文本塊和行塊信息text_blocks = []line_blocks = []# 遍歷 PDF 中的每一頁for page_number in range(len(pdf_document)):page = pdf_document.load_page(page_number)# 獲取文本塊和行塊信息blocks = page.get_text("dict")["blocks"]for b in blocks:for l in b["lines"]:line_blocks.append({"line": l["spans"],"bbox": l["bbox"],"height": l["bbox"][3] - l["bbox"][1] # 計算行塊的高度})text_blocks.append({"block": b["lines"],"bbox": b["bbox"]})# 關閉 PDF 文件pdf_document.close()return text_blocks, line_blocks# 示例用法
pdf_path = "D:\\angus\\py\\困難pdf節選西藏奇正2022.pdf"
text_blocks, line_blocks = extract_text_blocks(pdf_path)# 打印提取的文本塊信息
for index, block in enumerate(text_blocks):print(f"Text Block {index + 1}:")for line_index, line in enumerate(block["block"]):print(f" Line {line_index + 1}: '{line['spans']}' at position {block['bbox']}")# 打印提取的行塊信息
for index, line in enumerate(line_blocks):print(f"Line {index + 1}: '{line['line']}' at position {line['bbox']}, height={line['height']}")
三、效果展示
3.1 原文PDF內容
?3.2 解析后得到的結果
?3.3 分析原文和結果
對比輸出的結果和原文。我們可以發現,我們拿到了行的數據,也拿到了段落的數據。上述的代碼中已經給我們分好了塊!這樣解可以區分段落了。
3.4 獲取更多信息,包括位置
來看一個文本塊:
size
: 文本的大小。flags
: 文本的標志。font
: 字體名稱。color
: 字體顏色。ascender
: 文本的上升高度。descender
: 文本的下降高度。text
: 文本內容。origin
: 文本的起始位置坐標。bbox
: 文本的邊界框坐標,即左下角和右上角的坐標。
通過這些信息,我們可以獲取到每個文本塊的具體內容、大小、位置和格式等信息。這些信息對于分析和處理 PDF 文件中的文本內容非常有用。例如,你可以根據文本的大小、位置和格式來識別標題、正文和其他內容,并進行相應的處理和分析。當然,就以這個文檔為例,我們可以看到的是,因為文檔本身字體大小都一樣,所以很難根據字體和大小獲取到標題。
四、錯誤問題
?但是也發現了問題
4.1 段落有被分開了
原文
錯誤的問題如下
4.2 將表格錯當成了文本內容
原文表格內容如下
?
解析得到的內容如下
表格的一行為一個塊內容,
這里調試了一版,可以去掉表格。
邏輯是:判斷相鄰的block,表格的特征是,當個block內的 lines的 bbox的第四位是相同的。且相鄰的block的lines一定是相同的,且lines不為空。邏輯本身沒有問題,就怕PDF有問題,識別出來的表格的同一行的bbox中的第四位不一樣,這樣會錯誤判斷!
import fitz # PyMuPDFdef is_table_block(b1, b2):# 檢查連續相鄰的文本塊是否具有相同的行數,并且其 bbox 的高度也相同if len(b1["lines"]) == len(b2["lines"]) and b1["bbox"][3] - b1["bbox"][1] == b2["bbox"][3] - b2["bbox"][1]:return Truereturn Falsedef extract_text_blocks(pdf_path):# 打開 PDF 文件pdf_document = fitz.open(pdf_path)# 存儲文本塊信息text_blocks = []line_blocks = []# 遍歷 PDF 中的每一頁for page_number in range(len(pdf_document)):page = pdf_document.load_page(page_number)# 獲取文本塊和行塊信息blocks = page.get_text("dict")["blocks"]for i in range(len(blocks)):if i < len(blocks) - 1 and is_table_block(blocks[i], blocks[i+1]): # 如果是表格,則跳過continuefor l in blocks[i]["lines"]:line_blocks.append({"line": l["spans"],"bbox": l["bbox"],"height": l["bbox"][3] - l["bbox"][1] # 計算行塊的高度})text_blocks.append({"block": blocks[i]["lines"],"bbox": blocks[i]["bbox"]})# 關閉 PDF 文件pdf_document.close()return text_blocks, line_blocks# 示例用法
pdf_path = "D:\\angus\\py\\困難pdf節選西藏奇正2022.pdf"
text_blocks, line_blocks = extract_text_blocks(pdf_path)# 打印提取的文本塊信息
# 用于檢查兩個文本塊中的行是否相同
def check_lines_same(block1, block2):num_lines_block1 = len(block1["block"])num_lines_block2 = len(block2["block"])return num_lines_block1 == num_lines_block2for index, block in enumerate(text_blocks):# 獲取當前文本塊中行的個數num_lines = len(block["block"])# 如果當前文本塊是表格,則繼續檢查下一個文本塊是否是表格if num_lines > 1 and index < len(text_blocks) - 1: # 需要多于一行,并且不是最后一個文本塊next_block = text_blocks[index + 1]if check_lines_same(block, next_block):# 如果下一個文本塊也是表格,則跳過,不進行打印輸出continue# 如果當前文本塊不是表格,則打印輸出print(f"Text Block {index + 1}:")for line_index, line in enumerate(block["block"]):print(f" Line {line_index + 1}: '{line['spans']}' at position {block['bbox']}")# 打印提取的行塊信息
# for index, line in enumerate(line_blocks):
# print(f"Line {index + 1}: '{line['line']}' at position {line['bbox']}, height={line['height']}")
4.3 解析丟失整行數據
測試了另外一個法律法規文件。
發現文件丟失了。原文件內容如下:
解析后的:
還沒找到bug的原因。?