Python:解析PDF文本及表格——pdfminer、tabula、pdfplumber 的用法及對比

PDF 是個異常坑爹的東西,有不少處理 PDF 的庫,可是沒有完美的。java

1、pdfminer3k

pdfminer3kpdfminerpython3 版本,主要用於讀取 PDF 中的文本。python

網上有不少 pdfminer3k 的代碼示例,看過之後,只想吐槽一下,太複雜了,有違 python 的簡潔。數組

from pdfminer.pdfparser import PDFParser, PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox
from pdfminer.pdfinterp import PDFTextExtractionNotAllowed

path = "test.pdf"

# 用文件對象來建立一個pdf文檔分析器
praser = PDFParser(open(path, 'rb'))
# 建立一個PDF文檔
doc = PDFDocument()
# 鏈接分析器 與文檔對象
praser.set_document(doc)
doc.set_parser(praser)

# 提供初始化密碼
# 若是沒有密碼 就建立一個空的字符串
doc.initialize()

# 檢測文檔是否提供txt轉換,不提供就忽略
if not doc.is_extractable:
    raise PDFTextExtractionNotAllowed
else:
    # 建立PDf 資源管理器 來管理共享資源
    rsrcmgr = PDFResourceManager()
    # 建立一個PDF設備對象
    laparams = LAParams()
    device = PDFPageAggregator(rsrcmgr, laparams=laparams)
    # 建立一個PDF解釋器對象
    interpreter = PDFPageInterpreter(rsrcmgr, device)

    # 循環遍歷列表,每次處理一個page的內容
    for page in doc.get_pages():
        interpreter.process_page(page)                        
        # 接受該頁面的LTPage對象
        layout = device.get_result()
        # 這裏layout是一個LTPage對象,裏面存放着這個 page 解析出的各類對象
        # 包括 LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等 
        for x in layout:
            if isinstance(x, LTTextBox):
                print(x.get_text().strip())
複製代碼

pdfminer 對於表格的處理很是的不友好,能提取出文字,可是沒有格式:app

PDF 表格截圖:工具

代碼運行結果:spa

想把這個結果還原成表格可不容易,加的規則太多必然致使通用性的降低。code

2、tabula-py

tabula 是專門用來提取 PDF 表格數據的,同時支持 PDF 導出爲 CSV、Excel 格式,可是這工具是用 java 寫的,依賴 java7/8。tabula-py 就是對它作了一層 Python 的封裝,因此也依賴 java7/8。cdn

代碼很簡單:對象

import tabula

path = 'test.pdf'

df = tabula.read_pdf(path, encoding='gbk', pages='all')
for indexs in df.index:
    print(df.loc[indexs].values)

# tabula.convert_into(path, os.path.splitext(path)[0]+'.csv', pages='all')
複製代碼

雖然號稱是專業處理 PDF 中的表格的,但實際效果也不咋地。仍是 pdfminer 中使用的 PDF,運行結果以下:blog

這結果然的很尷尬啊,表頭識別就錯了,還有 PDF 中有兩張表,我沒發現怎麼區分表。

3、pdfplumber

pdfplumber 是按頁來處理 PDF 的,能夠得到頁面的全部文字,而且提供的單獨的方法用於提取表格。

import pdfplumber

path = 'test.pdf'
pdf = pdfplumber.open(path)

for page in pdf.pages:
    # 獲取當前頁面的所有文本信息,包括表格中的文字
    # print(page.extract_text()) 

    for table in page.extract_tables():
        # print(table)
        for row in table:
            print(row)
        print('---------- 分割線 ----------')

pdf.close()
複製代碼

獲得的 table 是個 string 類型的二維數組,這裏爲了跟 tabula 比較,按行輸出顯示。

能夠看到,跟 tabula 相比,首先是能夠區分表格,其次,準確率也提升了不少,表頭的識別徹底正確。對於表格中有換行的,識別還不是很正確,但至少列的劃分沒問題,因此仍是能處理的。

import pdfplumber
import re

path = 'test1.pdf'
pdf = pdfplumber.open(path)

for page in pdf.pages:
    print(page.extract_text())
    for pdf_table in page.extract_tables():
        table = []
        cells = []
        for row in pdf_table:
            if not any(row):
                # 若是一行全爲空,則視爲一條記錄結束
                if any(cells):
                    table.append(cells)
                    cells = []
            elif all(row):
                # 若是一行全不爲空,則本條爲新行,上一條結束
                if any(cells):
                    table.append(cells)
                    cells = []
                table.append(row)
            else:
                if len(cells) == 0:
                    cells = row
                else:
                    for i in range(len(row)):
                        if row[i] is not None:
                            cells[i] = row[i] if cells[i] is None else cells[i] + row[i]
        for row in table:
            print([re.sub('\s+', '', cell) if cell is not None else None for cell in row])
        print('---------- 分割線 ----------')

pdf.close()
複製代碼

通過處理後,運行獲得結果:

image

這結果已經徹底正確了,而用 tabula,即使是通過處理也是沒法獲得這樣的結果的。固然對於不一樣的 PDF,可能須要不一樣的處理,實際狀況仍是要本身分析。

pdfplumber 也有處理不許確的時候,主要表如今缺列:

我找了另外一個 PDF,表格部分截圖以下:

解析結果以下:

4 列變成了 2 列,另外,若是表格有合併單元格的狀況,也會有這種問題,我挑這個表格展現是由於比較特殊,沒有合併單元格也缺列了。這應該跟 PDF 生成的時候有關。

但其實數據是獲取完整的,並無丟,只是被認爲是非表格了。輸出 page.extract_text() 以下:

而後,我又用 tabula 試了下,結果以下:

列是齊了,可是,表頭呢???

pdfplumber 還提供了圖形 Debug 功能,能夠得到 PDF 頁面的截圖,而且用方框框起識別到的文字或表格,幫助判斷PDF的識別狀況,而且進行配置的調整。要使用這個功能,還須要安裝 ImageMagick。由於沒有用到,因此暫時沒有去細究。

4、後記

咱們在作爬蟲的時候,不免會遇到 PDF 須要解析,主要仍是針對文本和表格的數據提取。而 python 處理 PDF 的庫實在是太多太多了,好比還有 pypdf2,網上資料也比較多,可是我試了,讀出來是亂碼,沒有仔細的讀源碼因此這個問題也沒有解決。

而我對比較經常使用的 3 個庫比較後以爲,仍是 pdfplumber 比較好用,對錶格的支持最好。


掃碼關注個人我的公衆號
相關文章
相關標籤/搜索