最強的Python 辦公自動化之 PDF 攻略來了(全)

個小節展開:

相關介紹

批次拆分

批次合併

提取文字內容

提起表格內容

提起圖片內容

轉換為PDF圖片

新增水印

加密與解碼

上述操作比較常用,也可以解決較多的辦公內容,下面直接開始本節內容:

1。 相關介紹

Python 操作 PDF 會用到兩個庫,分別是:PyPDF2 和 pdfplumber

其中

PyPDF2

可以更好的讀取、寫入、分割、合併PDF檔案,而

pdfplumber

可以更好的讀取 PDF 檔案中內容和提取 PDF 中的表格

對應的官網分別是:

PyPDF2:https://pythonhosted。org/PyPDF2/

pdfplumber:https://github。com/jsvine/pdfplumber

由於這兩個庫都不是 Python 的標準庫,所以在使用之前都需要單獨安裝

win+r 後輸入 cmd 開啟 command 視窗,依次輸入如下命令進行安裝:

pip install PyPDF2

pip install pdfplumber

安裝完成後顯示 success 則表示安裝成功

最強的Python 辦公自動化之 PDF 攻略來了(全)

2。 批次拆分

將一個完整的 PDF 拆分成幾個小的 PDF,因為主要涉及到 PDF 整體的操作,所以本小節需要用到 PyPDF2 這個庫

拆分的大概思路如下:

讀取 PDF 的整體資訊、總頁數等

遍歷每一頁內容,以每個 step 為間隔將 PDF 存成每一個小的檔案塊

將小的檔案塊重新儲存為新的 PDF 檔案

需要注意的是,在拆分的過程中,可以手動設定間隔,例如:每5頁儲存成一個小的 PDF 檔案

拆分的程式碼如下:

import

os

from

PyPDF2

import

PdfFileWriter, PdfFileReader

def

split_pdf

(filename, filepath, save_dirpath, step=

5

“”“

拆分PDF為多個小的PDF檔案,

@param filename:檔名

@param filepath:檔案路徑

@param save_dirpath:儲存小的PDF的檔案路徑

@param step: 每step間隔的頁面生成一個檔案,例如step=5,表示0-4頁、5-9頁。。。為一個檔案

@return:

”“”

if

not

os。path。exists(save_dirpath):

os。mkdir(save_dirpath)

pdf_reader = PdfFileReader(filepath)

# 讀取每一頁的資料

pages = pdf_reader。getNumPages()

for

page

in

range(

0

, pages, step):

pdf_writer = PdfFileWriter()

# 拆分pdf,每 step 頁的拆分為一個檔案

for

index

in

range(page, page+step):

if

index < pages:

pdf_writer。addPage(pdf_reader。getPage(index))

# 儲存拆分後的小檔案

save_path = os。path。join(save_dirpath, filename+str(int(page/step)+

1

)+

‘。pdf’

print(save_path)

with

open(save_path,

“wb”

as

out:

pdf_writer。write(out)

print(

“檔案已成功拆分,儲存路徑為:”

+save_dirpath)

split_pdf(filename, filepath, save_dirpath, step=

5

以“易方達中小盤混合型證券投資基金2020年中期報告”為例,整個 PDF 檔案一共 46 頁,每5頁為間隔,最終生成了10個小的 PDF 檔案

最強的Python 辦公自動化之 PDF 攻略來了(全)

3。 批次合併

比起拆分來,合併的思路更加簡單:

確定要合併的

檔案順序

迴圈追加到一個檔案塊中

儲存成一個新的檔案

對應的程式碼比較簡單:

import

os

from

PyPDF2

import

PdfFileReader, PdfFileWriter

def

concat_pdf

(filename, read_dirpath, save_filepath)

“”“

合併多個PDF檔案

@param filename:檔名

@param read_dirpath:要合併的PDF目錄

@param save_filepath:合併後的PDF檔案路徑

@return:

”“”

pdf_writer = PdfFileWriter()

# 對檔名進行排序

list_filename = os。listdir(read_dirpath)

list_filename。sort(key=

lambda

x: int(x[:

-4

]。replace(filename,

“”

)))

for

filename

in

list_filename:

print(filename)

filepath = os。path。join(read_dirpath, filename)

# 讀取檔案並獲取檔案的頁數

pdf_reader = PdfFileReader(filepath)

pages = pdf_reader。getNumPages()

# 逐頁新增

for

page

in

range(pages):

pdf_writer。addPage(pdf_reader。getPage(page))

# 儲存合併後的檔案

with

open(save_filepath,

“wb”

as

out:

pdf_writer。write(out)

print(

“檔案已成功合併,儲存路徑為:”

+save_filepath)

concat_pdf(filename, read_dirpath, save_filepath)

4。 提取文字內容

涉及到具體的 PDF 內容 操作,本小節需要用到 pdfplumber 這個庫

在進行文字提取的時候,主要用到 extract_text 這個函式

具體程式碼如下:

import

os

import

pdfplumber

def

extract_text_info

(filepath)

“”“

提取PDF中的文字

@param filepath:檔案路徑

@return:

”“”

with

pdfplumber。open(filepath)

as

pdf:

# 獲取第2頁資料

page = pdf。pages[

1

print(page。extract_text())

# 提取文字內容

extract_text_info(filepath)

可以看到,直接透過下標即可定位到相應的頁碼,從而透過 extract_text 函式提取該也的所有文字

而如果想要提取所有頁的文字,只需要改成:

with

pdfplumber。open(filepath)

as

pdf:

# 獲取全部資料

for

page

in

pdf。pages

print(page。extract_text())

例如,提取“易方達中小盤混合型證券投資基金2020年中期報告” 第一頁的內容時,原始檔是這樣的:

最強的Python 辦公自動化之 PDF 攻略來了(全)

執行程式碼後提取出來是這樣的:

最強的Python 辦公自動化之 PDF 攻略來了(全)

5。 提取表格內容

同樣的,本節是對具體內容的操作,所以也需要用到 pdfplumber 這個庫

和提取文字十分類似的是,提取表格內容只是將 extract_text 函式換成了 extract_table 函式

對應的程式碼如下:

import

os

import

pandas

as

pd

import

pdfplumber

def

extract_table_info

(filepath)

“”“

提取PDF中的圖表資料

@param filepath:

@return:

”“”

with

pdfplumber。open(filepath)

as

pdf:

# 獲取第18頁資料

page = pdf。pages[

17

# 如果一頁有一個表格,設定表格的第一行為表頭,其餘為資料

table_info = page。extract_table()

df_table = pd。DataFrame(table_info[

1

:], columns=table_info[

0

])

df_table。to_csv(

‘dmeo。csv’

, index=

False

, encoding=

‘gbk’

# 提取表格內容

extract_table_info(filepath)

上面程式碼可以獲取到第 18 頁的第一個表格內容,並且將其儲存為 csv 檔案存在本地

但是,如果說第 18 頁有多個表格內容呢?

因為讀取的表格會被存成二維陣列,而多個二維陣列就組成一個三維陣列

遍歷這個三位陣列,就可以得到該頁的每一個表格資料,對應的將 extract_table 函式 改成 extract_tables 即可

具體程式碼如下:

# 如果一頁有多個表格,對應的資料是一個三維陣列

tables_info = page。extract_tables()

for

index

in

range(len(tables_info)):

# 設定表格的第一行為表頭,其餘為資料

df_table = pd。DataFrame(tables_info[index][

1

:], columns=tables_info[index][

0

])

print(df_table)

# df_table。to_csv(‘dmeo。csv’, index=False, encoding=‘gbk’)

以“易方達中小盤混合型證券投資基金2020年中期報告” 第 xx 頁的第一個表格為例:

原始檔中的表格是這樣的:

最強的Python 辦公自動化之 PDF 攻略來了(全)

提取並存入 excel 之後的表格是這樣的:

最強的Python 辦公自動化之 PDF 攻略來了(全)

6。 提取圖片內容

提取 PDF 中的圖片和將 PDF 轉存為圖片是不一樣的(下一小節),需要區分開。

提取圖片

:顧名思義,就是將內容中的圖片都提取出來;

轉存為圖片

:則是將每一頁的 PDF 內容存成一頁一頁的圖片,下一小節會詳細說明

轉存為圖片中,需要用到一個模組叫 fitz,fitz 的最新版 1。18。13,非最新版的在部分函式名稱上存在差異,程式碼中會標記出來

使用 fitz 需要先安裝 PyMuPDF 模組,安裝方式如下:

pip install PyMuPDF

提取圖片的整體邏輯如下:

使用 fitz 開啟文件,獲取文件詳細資料

遍歷每一個元素,透過正則找到圖片的索引位置

使用 Pixmap 將索引對應的元素生成圖片

透過 size 函式過濾較小的圖片

實現的具體程式碼如下:

import

os

import

re

import

fitz

def

extract_pic_info

(filepath, pic_dirpath)

“”“

提取PDF中的圖片

@param filepath:pdf檔案路徑

@param pic_dirpath:要儲存的圖片目錄路徑

@return:

”“”

if

not

os。path。exists(pic_dirpath):

os。makedirs(pic_dirpath)

# 使用正則表示式來查詢圖片

check_XObject =

r“/Type(?= */XObject)”

check_Image =

r“/Subtype(?= */Image)”

img_count =

0

“”“1。 開啟pdf,列印相關資訊”“”

pdf_info = fitz。open(filepath)

# 1。16。8版本用法 xref_len = doc。_getXrefLength()

# 最新版本

xref_len = pdf_info。xref_length()

# 列印PDF的資訊

print(

“檔名:{}, 頁數: {}, 物件: {}”

。format(filepath, len(pdf_info), xref_len

-1

))

“”“2。 遍歷PDF中的物件,遇到是影象才進行下一步,不然就continue”“”

for

index

in

range(

1

, xref_len):

# 1。16。8版本用法 text = doc。_getXrefString(index)

# 最新版本

text = pdf_info。xref_object(index)

is_XObject = re。search(check_XObject, text)

is_Image = re。search(check_Image, text)

# 如果不是物件也不是圖片,則不操作

if

is_XObject

or

is_Image:

img_count +=

1

# 根據索引生成影象

pix = fitz。Pixmap(pdf_info, index)

pic_filepath = os。path。join(pic_dirpath,

‘img_’

+ str(img_count) +

‘。png’

“”“pix。size 可以反映畫素多少,簡單的色素塊該值較低,可以透過設定一個閾值過濾。以閾值 10000 為例過濾”“”

# if pix。size < 10000:

#     continue

“”“三、 將影象存為png格式”“”

if

pix。n >=

5

# 先轉換CMYK

pix = fitz。Pixmap(fitz。csRGB, pix)

# 存為PNG

pix。writePNG(pic_filepath)

# 提取圖片內容

extract_pic_info(filepath, pic_dirpath)

以本節示例的“易方達中小盤混合型證券投資基金2020年中期報告” 中的圖片為例,程式碼執行後提取的圖片如下:

最強的Python 辦公自動化之 PDF 攻略來了(全)

這個結果和文件中的共 1 張圖片的

結果符合

7。 轉換為圖片

轉換為照片比較簡單,就是將一頁頁的 PDF 轉換為一張張的圖片。大致過程如下:

安裝 pdf2image

首先需要安裝對應的庫,最新的 pdf2image 庫版本應該是 1。14。0

它的 github地址 為:

https://github。com/Belval/pdf2image

,感興趣的可以自行了解

安裝方式如下:

pip install pdf2image

安裝元件

對於不同的平臺,需要安裝相應的元件,這裡以 windows 平臺和 mac 平臺為例:

Windows 平臺

對於 windows 使用者需要安裝 poppler for Windows,安裝連結是:

http://blog。alivate。com。au/poppler-windows/

另外,還需要新增環境變數, 將 bin 資料夾的路徑新增到環境變數 PATH 中

注意這裡配置之後需要重啟一下電腦才會生效,不然會報錯

Mac

對於 mac 使用者,需要安裝 poppler for Mac,具體可以參考這個連結:

http://macappstore。org/poppler/

詳細程式碼如下:

import

os

from

pdf2image

import

convert_from_path, convert_from_bytes

def

convert_to_pic

(filepath, pic_dirpath)

“”“

每一頁的PDF轉換成圖片

@param filepath:pdf檔案路徑

@param pic_dirpath:圖片目錄路徑

@return:

”“”

print(filepath)

if

not

os。path。exists(pic_dirpath):

os。makedirs(pic_dirpath)

images = convert_from_bytes(open(filepath,

‘rb’

)。read())

# images = convert_from_path(filepath, dpi=200)

for

image

in

images:

# 儲存圖片

pic_filepath = os。path。join(pic_dirpath,

‘img_’

+str(images。index(image))+

‘。png’

image。save(pic_filepath,

‘PNG’

# PDF轉換為圖片

convert_to_pic(filepath, pic_dirpath)

以本節示例的“易方達中小盤混合型證券投資基金2020年中期報告” 中的圖片為例,該文件共 46 頁,儲存後的 PDF 照片如下:

最強的Python 辦公自動化之 PDF 攻略來了(全)

一共 46 張圖片

8。 新增水印

新增水印後的效果如下:

最強的Python 辦公自動化之 PDF 攻略來了(全)

在製作水印的時候,可以自定義水印內容、透明度、斜度、字間寬度等等,可操作性比較好。

前面

專門

寫過一篇文章

,講的特別詳細:

Python快速給PDF檔案新增自定義水印

9。 文件加密與解密

你可能在開啟部分 PDF 檔案的時候,會彈出下面這個介面:

最強的Python 辦公自動化之 PDF 攻略來了(全)

這種就是 PDF 檔案被加密了,在開啟的時候需要相應的密碼才行

本節所提到的也只是基於 PDF 文件的加密解密,而不是所謂的 PDF 密碼破解。

在對 PDF 檔案加密需要使用 encrypt 函式,對應的加密程式碼也比較簡單:

import

os

from

PyPDF2

import

PdfFileReader, PdfFileWriter

def

encrypt_pdf

(filepath, save_filepath, passwd=

‘xiaoyi’

“”“

PDF文件加密

@param filepath:PDF檔案路徑

@param save_filepath:加密後的檔案儲存路徑

@param passwd:密碼

@return:

”“”

pdf_reader = PdfFileReader(filepath)

pdf_writer = PdfFileWriter()

for

page_index

in

range(pdf_reader。getNumPages()):

pdf_writer。addPage(pdf_reader。getPage(page_index))

# 新增密碼

pdf_writer。encrypt(passwd)

with

open(save_filepath,

“wb”

as

out:

pdf_writer。write(out)

# 文件加密

encrypt_pdf(filepath, save_filepath, passwd=

‘xiaoyi’

程式碼執行成功後再次開啟 PDF 檔案則需要輸入密碼才行

根據這個思路,破解 PDF 也可以透過暴力求解實現,例如:透過本地密碼本一個個去嘗試,或者根據數字+字母的密碼形式迴圈嘗試,最終成功開啟的密碼就是破解密碼

上述破解方法耗時耗力,不建議嘗試

另外,針對已經加密的 PDF 檔案,也可以使用 decrypt 函式進行解密操作

解密程式碼如下:

def

decrypt_pdf

(filepath, save_filepath, passwd=

‘xiaoyi’

“”“

解密 PDF 文件並且儲存為未加密的 PDF

@param filepath:PDF檔案路徑

@param save_filepath:解密後的檔案儲存路徑

@param passwd:密碼

@return:

”“”

pdf_reader = PdfFileReader(filepath)

# PDF文件解密

pdf_reader。decrypt(

‘xiaoyi’

pdf_writer = PdfFileWriter()

for

page_index

in

range(pdf_reader。getNumPages()):

pdf_writer。addPage(pdf_reader。getPage(page_index))

with

open(save_filepath,

“wb”

as

out:

pdf_writer。write(out)

# 文件解密

decrypt_pdf(filepath, save_filepath, passwd=

‘xiaoyi’

解密完成後的 PDF 文件開啟後不再需要輸入密碼,如需加密可再次執行加密程式碼。

相關文章