Python 自動(dòng)化辦公 ——— 用 PyPDF2 庫對(duì) PDF 實(shí)現(xiàn)拆分、合并、水印添加、加密解密操作

大家好,我是小張~,今天文章與自動(dòng)化辦公相關(guān),目前個(gè)人認(rèn)為 Python 庫中處理 PDF 比較不錯(cuò)的有三個(gè),分別是 PyPDF2,Pdfplumer 和 PDFminer;

image-20210313210858337

今天教程內(nèi)容主要聚焦于 PyPDF2 ,借助它對(duì) PDF 實(shí)現(xiàn)以下基本操作

  • 1,將單個(gè) PDF 拆分為多個(gè) PDF 文件 ;

  • 2,將多個(gè) PDF 合并為一個(gè) PDF 文件 ;

  • 3,將 PDF 中某頁進(jìn)行旋轉(zhuǎn) ;

  • 4,對(duì) PDF 添加水印 ;

  • 5,對(duì) PDF 加密 ;

  • 6,對(duì) PDF 進(jìn)行解密;

  • 6,獲取 PDF 基本信息,例如作者、標(biāo)題、頁數(shù)等;

PyPDF2 歷史

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

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

PyPDF2 安裝

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

pip install pypdf2

PDF 信息提取

使用 PyPDF2 可以從 PDF 中提取到一些元數(shù)據(jù)和文本信息,對(duì) PDF 有個(gè)大致了解

用 PyPDF2 能夠提取的數(shù)據(jù)如下

  • 作者;

  • 創(chuàng)建者;

  • 制作者;

  • Subject;

  • 標(biāo)題;

  • 頁數(shù);

這里我下載了官網(wǎng)提供的 PDF 樣本《Seige_of_Vicksburg_Sample_OCR》一共六頁,作為測試數(shù)據(jù)

image-20210313230206113
from  PyPDF2 import PdfFileReader


# # pdf 文檔
pdf_path = "D:/Data/自動(dòng)化辦公/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)

下面為打印結(jié)果

D:/Data/自動(dòng)化辦公/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 文件交互;調(diào)用該類中的 getDocumentInfo() 方法返回一個(gè) DocumentInformation 的實(shí)例,該實(shí)例中存儲(chǔ)著我們需要的信息;對(duì) reader 對(duì)象調(diào)用 getNumPages 方法也可以返回文檔頁數(shù);

個(gè)人看法,這里面的數(shù)據(jù)也就 頁數(shù) 有點(diǎn)價(jià)值,當(dāng)批量統(tǒng)計(jì)時(shí)該方法很適用

PDF 頁面旋轉(zhuǎn)

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

對(duì)某一頁旋轉(zhuǎn),有兩種方式

  • rotateClockwise(90),順時(shí)針旋轉(zhuǎn)90度;

  • rotateCounterClockwise(90),逆時(shí)針旋轉(zhuǎn) 90 度;

下面代碼表示將目標(biāo) PDF 中第一頁順時(shí)針方向旋轉(zhuǎn) 90 度,第二頁以逆時(shí)針方向旋轉(zhuǎn) 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)

結(jié)果如下

image-20210313232532349

代碼中同時(shí)用到了PdfFileReader,PdfFileWriter 這兩個(gè)類,頁面旋轉(zhuǎn)并不是在原有 PDF 基礎(chǔ)上進(jìn)行操作而是在內(nèi)存處創(chuàng)建了一個(gè)新的PDF流對(duì)象,將操作后的每一頁通過 addPage() 方法加入到這個(gè)對(duì)象中,之后將內(nèi)存中的這個(gè)對(duì)象寫入到文件中;

寫到這里,說實(shí)話其實(shí) 頁面旋轉(zhuǎn) 這個(gè)功能沒基本沒什么作用,加在這里只是想充當(dāng)一些字?jǐn)?shù),哈哈哈

單個(gè) PDF 拆分成多個(gè)PDF

from  PyPDF2 import PdfFileReader,PdfFileWriter

# # pdf 文檔
pdf_path = "D:/Data/自動(dòng)化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"
save_path = 'D:/Data/自動(dòng)化辦公/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 原文件中的每一頁拆分到每一個(gè)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)

多個(gè) PDF 文件合并為單個(gè)

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

PdfFileReader讀取每個(gè)pdf,并遞歸獲取每一頁page 對(duì)象, PdfFileWrite 新建一個(gè)流對(duì)象,把前面內(nèi)存中讀取到的 page 對(duì)象按順序?qū)懭氲竭@個(gè)流對(duì)象中,最后寫入到磁盤文件

···
from PyPDF2 import PdfFileReader,PdfFileWriter

p1_pdf = "D:/Data/自動(dòng)化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"
p2_pdf = "D:/Data/自動(dòng)化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf"

merge_pdf = 'D:/Data/自動(dòng)化辦公/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)
···

運(yùn)行結(jié)果如下

image-20210314002536754

PDF 添加水印

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

因?yàn)?PyPDF2 只能操作 pdf 對(duì)象,因此在添加水印之前,需要將準(zhǔn)備添加的水印存放到一個(gè) pdf 文件中

···
from PyPDF2 import PdfFileReader,PdfFileWriter
watermark = 'D:/Data/自動(dòng)化辦公/PDF/watermark.pdf'
input_pdf = 'D:/Data/自動(dòng)化辦公/PDF/merge.pdf'
output = 'D:/Data/自動(dòng)化辦公/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)
···

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

上面效果不好是因?yàn)橹谱魉r(shí)沒有考慮到頁面布局問題,所以合并時(shí)出現(xiàn)一部分缺失;

用以上代碼添加水印的好處是,可以對(duì) pdf 指定頁田間水印,比如說只對(duì)奇數(shù)頁添加偶數(shù)頁不管,不但靈活性強(qiáng)而且高效,當(dāng)然也可以對(duì)多個(gè)文件進(jìn)行批量操作

PDF加密解密

pdf加密

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

···
watermark = 'D:/Data/自動(dòng)化辦公/PDF/Seige_of_Vicksburg_Sample_OCR.pdf'
input_pdf = 'D:/Data/自動(dòng)化辦公/PDF/merge.pdf'
output = 'D:/Data/自動(dòng)化辦公/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 函數(shù),需要注意三個(gè)參數(shù)

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

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

  • use_128bit 布爾值,用來表示是否使用128位作為密碼,F(xiàn)alse 時(shí)代表用 40 位密碼,默認(rèn)為True;

pdf解密

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

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)

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

小結(jié)

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

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容