Python 自動化辦公 ——— 用 PyPDF2 庫對 PDF 實現拆分、合并、水印添加、加密解密操作

大家好,我是小張~,今天文章與自動化辦公相關,目前個人認為 Python 庫中處理 PDF 比較不錯的有三個,分別是 PyPDF2,Pdfplumer 和 PDFminer;

image-20210313210858337

今天教程內容主要聚焦于 PyPDF2 ,借助它對 PDF 實現以下基本操作

  • 1,將單個 PDF 拆分為多個 PDF 文件 ;

  • 2,將多個 PDF 合并為一個 PDF 文件 ;

  • 3,將 PDF 中某頁進行旋轉 ;

  • 4,對 PDF 添加水印 ;

  • 5,對 PDF 加密 ;

  • 6,對 PDF 進行解密;

  • 6,獲取 PDF 基本信息,例如作者、標題、頁數等;

PyPDF2 歷史

正文開始之前,說一下 PyPDF2 的發展歷史 ,PyPDF 的前身是 pyPDf 包在2005年發布,該包的最后一個版本發布于2010年,后來大約經過一年左右, 名為 Phasit 的公司贊助 PyPdf 的一個分支后來命名為 PyPDF2,兩個版本功能都基本一樣,最大區別就是 PyPDF2 中 加入了支持 Python3 特性;

PyPDF2 近期也沒有再更新了,最近一個版本發布在2016年,但使用熱度依然沒有消退;雖然后面又出現了 PyPDF3、PyPDF4 等不同版本,但這些包并沒有對 PyPDF2 功能向后完全兼容,用戶受歡迎程度當然也不如 PyPDF2

PyPDF2 安裝

與其它Python 庫一樣,安裝可通過 pip 或 conda 工具

pip install pypdf2

PDF 信息提取

使用 PyPDF2 可以從 PDF 中提取到一些元數據和文本信息,對 PDF 有個大致了解

用 PyPDF2 能夠提取的數據如下

  • 作者;

  • 創建者;

  • 制作者;

  • Subject;

  • 標題;

  • 頁數;

這里我下載了官網提供的 PDF 樣本《Seige_of_Vicksburg_Sample_OCR》一共六頁,作為測試數據

image-20210313230206113
from  PyPDF2 import PdfFileReader


# # pdf 文檔
pdf_path = "D:/Data/自動化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"

with open(pdf_path,'rb') as f:
 pdf = PdfFileReader(f)
 infomation = pdf.getDocumentInfo()
 number_of_pages = pdf.getNumPages()

 txt = f'''{pdf_path} information:
 Author : {infomation.author},
 Creator : {infomation.creator},
 Producer : {infomation.producer},
 Subject : {infomation.subject},
 Title : {infomation.title},
 Number of pages : {number_of_pages}
 '''
 print(txt)

下面為打印結果

D:/Data/自動化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf information:
 Author : DSI,
 Creator : LuraDocument PDF Compressor Server 5.5.46.38,
 Producer : LuraDocument PDF v2.38,
 Subject : None,
 Title : Binder1.pdf,
 Number of pages : 6

在上面例子中用到了 PdfFileReader 類,用于與 pdf 文件交互;調用該類中的 getDocumentInfo() 方法返回一個 DocumentInformation 的實例,該實例中存儲著我們需要的信息;對 reader 對象調用 getNumPages 方法也可以返回文檔頁數;

個人看法,這里面的數據也就 頁數 有點價值,當批量統計時該方法很適用

PDF 頁面旋轉

PyPDF2 中 pdf 每一頁都是以 page 對象存在,返回某一頁的實例可通過 reader 對象中的 get_Page(page_index) 方法,其中 page_index 表示索引

對某一頁旋轉,有兩種方式

  • rotateClockwise(90),順時針旋轉90度;

  • rotateCounterClockwise(90),逆時針旋轉 90 度;

下面代碼表示將目標 PDF 中第一頁順時針方向旋轉 90 度,第二頁以逆時針方向旋轉 90 度,其它頁位置角度不變;

from  PyPDF2 import PdfFileReader,PdfFileWriter

pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(pdf_path)
# Rotate page 90 degrees to the right
page_1 = pdf_reader.getPage(0).rotateClockwise(90)
pdf_writer.addPage(page_1)
# Rotate page 90 degrees to the left
page_2 = pdf_reader.getPage(1).rotateCounterClockwise(90)
pdf_writer.addPage(page_2)
# 之后的正常寫出
for i in range(2,pdf_reader.getNumPages()):
 pdf_writer.addPage(pdf_reader.getPage(i))

with open(pdf_path, 'wb') as fh:
 pdf_writer.write(fh)

結果如下

image-20210313232532349

代碼中同時用到了PdfFileReader,PdfFileWriter 這兩個類,頁面旋轉并不是在原有 PDF 基礎上進行操作而是在內存處創建了一個新的PDF流對象,將操作后的每一頁通過 addPage() 方法加入到這個對象中,之后將內存中的這個對象寫入到文件中;

寫到這里,說實話其實 頁面旋轉 這個功能沒基本沒什么作用,加在這里只是想充當一些字數,哈哈哈

單個 PDF 拆分成多個PDF

from  PyPDF2 import PdfFileReader,PdfFileWriter

# # pdf 文檔
pdf_path = "D:/Data/自動化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"
save_path = 'D:/Data/自動化辦公/PDF/'

# Split Pages of PDF

pdf_reader = PdfFileReader(pdf_path)
for i in range(0,pdf_reader.getNumPages()):
 pdf_writer = PdfFileWriter()
 pdf_writer.addPage(pdf_reader.getPage(i))
 # Every page write to a path
 with open(save_path+'{}.pdf'.format(str(i)), 'wb') as fh:
 pdf_writer.write(fh)
 print('{} Save Sucessfully !\n'.format(str(i)))

代碼將 PDF 原文件中的每一頁拆分到每一個PDF文件,其中文件名用頁索引來命名;

image-20210313235957539

通過拆分也可以提取到 pdf 文件中固定頁碼范圍,例如我只想提取 pdf 中的 2-5 頁,其它部分不要,那么代碼將寫成下面形式

pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(pdf_path)
for i in range(1,5):
 # pdf_writer = PdfFileWriter()
 pdf_writer.addPage(pdf_reader.getPage(i))
 # Every page write to a path
with open(save_path+'2_5.pdf', 'wb') as fh:
 pdf_writer.write(fh)

多個 PDF 文件合并為單個

pdf 拆分與合并方向雖然相反,但用到的類、原理都是一樣的

PdfFileReader讀取每個pdf,并遞歸獲取每一頁page 對象, PdfFileWrite 新建一個流對象,把前面內存中讀取到的 page 對象按順序寫入到這個流對象中,最后寫入到磁盤文件

···
from PyPDF2 import PdfFileReader,PdfFileWriter

p1_pdf = "D:/Data/自動化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"
p2_pdf = "D:/Data/自動化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"

merge_pdf = 'D:/Data/自動化辦公/PDF/merge.pdf'

p1_reader = PdfFileReader(p1_pdf)
p2_reader = PdfFileReader(p2_pdf)

merge = PdfFileWriter()

Write p1

for i in range(0,p1_reader.getNumPages()):
merge.addPage(p1_reader.getPage(i))

Write p2

for j in range(0,p2_reader.getNumPages()):
merge.addPage(p2_reader.getPage(j))

Write out

with open(merge_pdf,'wb') as f:
merge.write(f)
···

運行結果如下

image-20210314002536754

PDF 添加水印

在今天列舉的這么多功能中,我想這個功能是最有用,批量添加水印主要用到 page 對象中的 margePage() 方法,通過將兩個頁面合并來達到添加水印的效果

因為 PyPDF2 只能操作 pdf 對象,因此在添加水印之前,需要將準備添加的水印存放到一個 pdf 文件中

···
from PyPDF2 import PdfFileReader,PdfFileWriter
watermark = 'D:/Data/自動化辦公/PDF/watermark.pdf'
input_pdf = 'D:/Data/自動化辦公/PDF/merge.pdf'
output = 'D:/Data/自動化辦公/PDF/merge_watermark.pdf'

watermark_obj = PdfFileReader(watermark)
watermark_page = watermark_obj.getPage(0)

pdf_reader = PdfFileReader(input_pdf)
pdf_writer = PdfFileWriter()

Watermark all the pages

for page in range(pdf_reader.getNumPages()):
page = pdf_reader.getPage(page)
page.mergePage(watermark_page)
pdf_writer.addPage(page)

with open(output, 'wb') as out:
pdf_writer.write(out)
···

效果如下,從左到右,依次為原圖、水印、添加水印后的原圖    
從左到右,依次是原圖、水印、加完水印的效果圖

上面效果不好是因為制作水印時沒有考慮到頁面布局問題,所以合并時出現一部分缺失;

用以上代碼添加水印的好處是,可以對 pdf 指定頁田間水印,比如說只對奇數頁添加偶數頁不管,不但靈活性強而且高效,當然也可以對多個文件進行批量操作

PDF加密解密

pdf加密

對一份 pdf 文件,如果我們不想讓其他人能夠讀取里面的內容,可以通過 pypdf2 對它設置密碼,如果只是單個文件的話,建議最好自己找個工具受手動操作一下會高效一點,但若是多個文件,非常建議用下面方法

···
watermark = 'D:/Data/自動化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf'
input_pdf = 'D:/Data/自動化辦公/PDF/merge.pdf'
output = 'D:/Data/自動化辦公/PDF/merge_watermark1.pdf'

watermark_obj = PdfFileReader(watermark)
watermark_page = watermark_obj.getPage(0)

pdf_reader = PdfFileReader(input_pdf)
pdf_writer = PdfFileWriter()

Watermark all the pages

for page in range(pdf_reader.getNumPages()):
page = pdf_reader.getPage(page)
page.mergePage(watermark_page)
pdf_writer.addPage(page)
pdf_writer.encrypt(user_pwd='123456',
use_128bit=True)
with open(output, 'wb') as out:
pdf_writer.write(out)
···

image-20210314092935806

主要用到 encrypt 函數,需要注意三個參數

  • user_pwd,str,用戶密碼,用來限制打開讀取文件;

  • owner_pwd,str,比用戶密碼更高一級,提供時可讓打開文件不受任何限制,不指定時默認owner_pwd 與 user_pwd 相同;

  • use_128bit 布爾值,用來表示是否使用128位作為密碼,False 時代表用 40 位密碼,默認為True;

pdf解密

解密是在讀取文件時用的,用到 decrypt() 函數

from PyPDF2 import PdfFileWriter, PdfFileReader

input_pdf='reportlab-encrypted.pdf'
output_pdf='reportlab.pdf'
password='twofish'

pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(input_pdf)
pdf_reader = pdf_reader.decrypt(password)

for page in range(pdf_reader.getNumPages()):
 pdf_writer.addPage(pdf_reader.getPage(page))

with open(output_pdf, 'wb') as fh:
 pdf_writer.write(fh)

上面例子中解密原理是 通過將一個加密文件進行讀取,并寫入到一個非加密 pdf 中

小結

本文介紹了 PyPDF2 庫的基本用法,借助它加上代碼實例實現了一些基本操作;但在這里提醒一下,所有上面這些操作只適用于批量操作場景,如果對象是單個文件的話建議用常規做法,過于炫技的話只會浪費時間

關于 pdf 內的圖文內容提取、寫入本文并沒有涉獵,源于 pypdf2 對于這方面并不擅長,而 Pdfplumber 和 PDFminer 在文本提取方面要好得多,工欲善其事,必先利其器;在之后的教程中我將會介紹一下這方面的內容,期待大家的關注!

好了以上就是本篇內容的全部內容,最后感謝大家的閱讀,我們下期見~

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容