如何用Python批量提取PDF文字內容?

王樹義發表於2018-06-27

如何用Python批量提取PDF文字內容?

本文為你展示,如何用Python把許多PDF檔案的文字內容批量提取出來,並且整理儲存到資料框中,以便於後續的資料分析。

問題

最近,讀者們在後臺的留言,愈發五花八門了。

寫了幾篇關於自然語言處理的文章後,一種呼聲漸強:

老師,pdf中的文字內容,有沒有什麼方便的方法提取出來呢?

我能體會到讀者的心情。

我展示的例子中,文字資料都是直接可以讀入資料框工具做處理的。它們可能來自開放資料集合、網站API,或者爬蟲。

但是,有的時候,你會遇到需要處理指定格式資料的問題。

例如pdf。

許多的學術論文、研究報告,甚至是資料分享,都採用這種格式釋出。

這時候,已經掌握了諸多自然語言分析工具的你,會頗有“拔劍四顧心茫然”的感覺——明明知道如何處理其中的文字資訊,但就是隔著一個格式轉換的問題,做不來。

怎麼辦?

辦法自然是有的,例如專用工具、線上轉換服務網站,甚至還可以手動複製貼上嘛。

但是,我們們是看重效率的,對不對?

上述辦法,有的需要在網上傳輸大量內容,花費時間較多,而且可能帶來安全和隱私問題;有的需要專門花錢購買;有的乾脆就不現實。

怎麼辦?

好訊息是,Python就可以幫助你高效、快速地批量提取pdf文字內容,而且和資料整理分析工具無縫銜接,為你後續的分析處理做好基礎服務工作。

本文給你詳細展示這一過程。

想不想試試?

資料

為了更好地說明流程,我為你準備好了一個壓縮包。

裡面包括本教程的程式碼,以及我們要用到的資料。

請你到 這個網址 下載本教程配套的壓縮包。

下載後解壓,你會在生成的目錄(下稱“演示目錄”)裡面看到以下內容。

如何用Python批量提取PDF文字內容?

演示目錄裡面包含:

  • Pipfile: pipenv 配置檔案,用來準備我們們變成需要用到的依賴包。後文會講解使用方法;
  • pdf_extractor.py: 利用pdfminer.six編寫的輔助函式。有了它你就可以直接呼叫pdfminer提供的pdf文字內容抽取功能,而不必考慮一大堆惱人的引數;
  • demo.ipynb: 已經為你寫好的本教程 Python 原始碼 (Jupyter Notebook格式)。

另外,演示目錄中還包括了2個資料夾。

這兩個資料夾裡面,都是中文pdf檔案,用來給你展示pdf內容抽取。它們都是我幾年前發表的中文核心期刊論文。

這裡做2點說明:

  1. 使用我自己的論文做示例,是因為我怕用別人的論文做文字抽取,會與論文作者及資料庫運營商之間有智慧財產權的糾紛;
  2. 分成2個資料夾,是為了向你展示新增新的pdf檔案時,抽取工具會如何處理。

pdf資料夾內容如下:

如何用Python批量提取PDF文字內容?

newpdf資料夾內容如下:

如何用Python批量提取PDF文字內容?

資料準備好了,下面我們來部署程式碼執行環境。

環境

要安裝Python,比較省事的辦法是裝Anaconda套裝。

請到 這個網址 下載Anaconda的最新版本。

如何用Python批量提取PDF文字內容?

請選擇左側的 Python 3.6 版本下載安裝。

如果你需要具體的步驟指導,或者想知道Windows平臺如何安裝並執行Anaconda命令,請參考我為你準備的 視訊教程

安裝好Anaconda之後,開啟終端,用cd命令進入演示目錄

如果你不瞭解具體使用方法,也可以參考 視訊教程

我們需要安裝一些環境依賴包。

首先執行:

pip install pipenv
複製程式碼

這裡安裝的,是一個優秀的 Python 軟體包管理工具 pipenv 。 安裝後,請執行:

pipenv install --skip-lock
複製程式碼

pipenv 工具會依照Pipfile,自動為我們安裝所需要的全部依賴軟體包。

終端裡面會有進度條,提示所需安裝軟體數量和實際進度。

裝好後,根據提示我們執行:

pipenv shell
複製程式碼

這樣,我們就進入本教程專屬的虛擬執行環境了。

注意一定要執行下面這句:

python -m ipykernel install --user --name=py36
複製程式碼

只有這樣,當前的Python環境才會作為核心(kernel)在系統中註冊,並且命名為py36。

此處請確認你的電腦上已經安裝了 Google Chrome 瀏覽器。

我們執行:

jupyter notebook
複製程式碼

預設瀏覽器(Google Chrome)會開啟,並啟動 Jupyter 筆記本介面:

如何用Python批量提取PDF文字內容?

你可以直接點選檔案列表中的第一項ipynb檔案,可以看到本教程的全部示例程式碼。

你可以一邊看教程的講解,一邊依次執行這些程式碼。

如何用Python批量提取PDF文字內容?

但是,我建議的方法,是回到主介面下,新建一個新的空白 Python 3 筆記本(顯示名稱為 py36 的那個)。

如何用Python批量提取PDF文字內容?

請跟著教程,一個個字元輸入相應的內容。這可以幫助你更為深刻地理解程式碼的含義,更高效地把技能內化。

如何用Python批量提取PDF文字內容?

當你在編寫程式碼中遇到困難的時候,可以返回參照 demo.ipynb 檔案。

準備工作結束,下面我們開始正式輸入程式碼。

程式碼

首先,我們讀入一些模組,以進行檔案操作。

import glob
import os
複製程式碼

前文提到過,演示目錄下,有兩個資料夾,分別是pdf和newpdf。

我們指定 pdf 檔案所在路徑為其中的pdf資料夾。

pdf_path = "pdf/"
複製程式碼

我們希望獲得所有 pdf 檔案的路徑。用glob,一條命令就能完成這個功能。

pdfs = glob.glob("{}/*.pdf".format(pdf_path))
複製程式碼

看看我們獲得的 pdf 檔案路徑是否正確。

pdfs
複製程式碼
['pdf/複雜系統模擬的微部落格虛假資訊擴散模型研究.pdf',
 'pdf/面向影子分析的社交媒體競爭情報蒐集.pdf',
 'pdf/面向人機協同的移動網際網路政務門戶探析.pdf']
複製程式碼

經驗證。準確無誤。

下面我們利用 pdfminer 來從 pdf 檔案中抽取內容。我們需要從輔助 Python 檔案 pdf_extractor.py 中讀入函式 extract_pdf_content

from pdf_extractor import extract_pdf_content
複製程式碼

用這個函式,我們嘗試從 pdf 檔案列表中的第一篇裡,抽取內容,並且把文字儲存在 content 變數裡。

content = extract_pdf_content(pdfs[0])
複製程式碼

我們看看 content 裡都有什麼:

content
複製程式碼

如何用Python批量提取PDF文字內容?

顯然,內容抽取並不完美,頁首頁尾等資訊都混了進來。

不過,對於我們的許多文字分析用途來說,這無關緊要。

你會看到 content 的內容裡面有許多的 \n,這是什麼呢?

我們用 print 函式,來顯示 content 的內容。

print(content)
複製程式碼

如何用Python批量提取PDF文字內容?

可以清楚看到,那些 \n 是換行符。

通過一個 pdf 檔案的抽取測試,我們建立了信心。

下面,我們該建立辭典,批量抽取和儲存內容了。

mydict = {}
複製程式碼

我們遍歷 pdfs 列表,把檔名稱(不包含目錄)作為鍵值。這樣,我們可以很容易看到,哪些pdf檔案已經被抽取過了,哪些還沒有抽取。

為了讓這個過程更為清晰,我們讓Python輸出正在抽取的 pdf 檔名。

for pdf in pdfs:
    key = pdf.split('/')[-1]
    if not key in mydict:
        print("Extracting content from {} ...".format(pdf))
        mydict[key] = extract_pdf_content(pdf)
複製程式碼

抽取過程中,你會看到這些輸出資訊:

Extracting content from pdf/複雜系統模擬的微部落格虛假資訊擴散模型研究.pdf ...
Extracting content from pdf/面向影子分析的社交媒體競爭情報蒐集.pdf ...
Extracting content from pdf/面向人機協同的移動網際網路政務門戶探析.pdf ...
複製程式碼

看看此時字典中的鍵值都有哪些:

mydict.keys()
複製程式碼
dict_keys(['複雜系統模擬的微部落格虛假資訊擴散模型研究.pdf', '面向影子分析的社交媒體競爭情報蒐集.pdf', '面向人機協同的移動網際網路政務門戶探析.pdf'])
複製程式碼

一切正常。

下面我們呼叫pandas,把字典變成資料框,以利於分析。

import pandas as pd
複製程式碼

下面這條語句,就可以把字典轉換成資料框了。注意後面的reset_index()把原先字典鍵值生成的索引也轉換成了普通的列。

df = pd.DataFrame.from_dict(mydict, orient='index').reset_index()
複製程式碼

然後我們重新命名列,以便於後續使用。

df.columns = ["path", "content"]
複製程式碼

此時的資料框內容如下:

df
複製程式碼

如何用Python批量提取PDF文字內容?

可以看到,我們的資料框擁有了pdf檔案資訊和全部文字內容。這樣你就可以使用關鍵詞抽取、情感分析、相似度計算等等諸多分析工具了。

篇幅所限,我們這裡只用一個字元數量統計的例子來展示基本分析功能。

我們讓 Python 幫我們統計抽取內容的長度。

df["length"] = df.content.apply(lambda x: len(x))
複製程式碼

此時的資料框內容發生以下變化:

df
複製程式碼

如何用Python批量提取PDF文字內容?

多出的一列,就是 pdf 文字內容的字元數量。

為了在 Jupyter Notebook 裡面正確展示繪圖結果,我們需要使用以下語句:

%matplotlib inline
複製程式碼

下面,我們讓 Pandas 把字元長度一列的資訊用柱狀圖示示出來。為了顯示的美觀,我們設定了圖片的長寬比例,並且把對應的pdf檔名稱以傾斜45度來展示。

import matplotlib.pyplot as plt
plt.figure(figsize=(14, 6))
df.set_index('path').length.plot(kind='bar')
plt.xticks(rotation=45)
複製程式碼

如何用Python批量提取PDF文字內容?

視覺化分析完成。

下面我們把剛才的分析流程整理成函式,以便於將來更方便地呼叫。

我們先整合pdf內容提取到字典的模組:

def get_mydict_from_pdf_path(mydict, pdf_path):
    pdfs = glob.glob("{}/*.pdf".format(pdf_path))
    for pdf in pdfs:
        key = pdf.split('/')[-1]
        if not key in mydict:
            print("Extracting content from {} ...".format(pdf))
            mydict[key] = extract_pdf_content(pdf)
    return mydict
複製程式碼

這裡輸入是已有詞典和pdf資料夾路徑。輸出為新的詞典。

你可能會納悶為何還要輸入“已有詞典”。彆著急,一會兒我用實際例子展示給你看。

下面這個函式非常直白——就是把詞典轉換成資料框。

def make_df_from_mydict(mydict):
    df = pd.DataFrame.from_dict(mydict, orient='index').reset_index()
    df.columns = ["path", "content"]
    return df
複製程式碼

最後一個函式,用於繪製統計出來的字元數量。

def draw_df(df):
    df["length"] = df.content.apply(lambda x: len(x))
    plt.figure(figsize=(14, 6))
    df.set_index('path').length.plot(kind='bar')
    plt.xticks(rotation=45)
複製程式碼

函式已經編好,下面我們來嘗試一下。

還記得演示目錄下有個子目錄,叫做newpdf對吧?

我們把其中的2個pdf檔案,移動到pdf目錄下面。

這樣pdf目錄下面,就有了5個檔案:

如何用Python批量提取PDF文字內容?

我們執行新整理出的3個函式。

首先輸入已有的詞典(注意此時裡面已有3條記錄),pdf資料夾路徑沒變化。輸出是新的詞典。

mydict = get_mydict_from_pdf_path(mydict, pdf_path)
複製程式碼
Extracting content from pdf/微部落格 Twitter 的企業競爭情報蒐集.pdf ...
Extracting content from pdf/移動社交媒體使用者隱私保護對策研究.pdf ...
複製程式碼

注意這裡的提示,原先的3個pdf檔案沒有被再次抽取,只有2個新pdf檔案被抽取。

我們們這裡一共只有5個檔案,所以你直觀上可能無法感受出顯著的區別。

但是,假設你原先已經用幾個小時,抽取了成百上千個pdf檔案資訊,結果你的老闆又丟給你3個新的pdf檔案……

如果你必須從頭抽取資訊,恐怕會很崩潰吧。

這時候,使用我們們的函式,你可以在1分鐘之內把新的檔案內容追加進去。

這差別,不小吧?

下面我們用新的詞典,構建資料框。

df = make_df_from_mydict(mydict)
複製程式碼

我們繪製新的資料框裡,pdf抽取文字字元數量。結果如下:

draw_df(df)
複製程式碼

如何用Python批量提取PDF文字內容?

至此,程式碼展示完畢。

小結

總結一下,本文為你介紹了以下知識點:

  • 如何用glob批量讀取目錄下指定格式的檔案路徑;
  • 如何用pdfminer從pdf檔案中抽取文字資訊;
  • 如何構建詞典,儲存與鍵值(本文中為檔名)對應的內容,並且避免重複處理資料;
  • 如何將詞典資料結構輕鬆轉換為Pandas資料框,以便於後續資料分析。
  • 如何用matplotlib和pandas自帶的繪圖函式輕鬆繪製柱狀統計圖形。

討論

你之前做的資料分析工作中,遇到過需要從pdf檔案抽取文字的任務嗎?你是如何處理的?有沒有更好的工具與方法?歡迎留言,把你的經驗和思考分享給大家,我們一起交流討論。

喜歡請點贊。還可以微信關注和置頂我的公眾號“玉樹芝蘭”(nkwangshuyi)

如果你對資料科學感興趣,不妨閱讀我的系列教程索引貼《如何高效入門資料科學?》,裡面還有更多的有趣問題及解法。

相關文章