大家好,我是小張~,今天文章與自動化辦公相關,目前個人認為 Python 庫中處理 PDF 比較不錯的有三個,分别是 PyPDF2,Pdfplumer 和 PDFminer;
今天教程内容主要聚焦于 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》一共六頁,作為測試資料
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)
結果如下
代碼中同時用到了
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檔案,其中檔案名用頁索引來命名;
通過拆分也可以提取到 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)
運作結果如下
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)
效果如下,從左到右,依次為原圖、水印、添加水印後的原圖 [外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-XP0ELXsk-1615825692823)(https://images.zeroingpython.top/img/image-20210314005417135.png)]
上面效果不好是因為制作水印時沒有考慮到頁面布局問題,是以合并時出現一部分缺失;
用以上代碼添加水印的好處是,可以對 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)
主要用到 encrypt 函數,需要注意三個參數
- user_pwd,str,使用者密碼,用來限制打開讀取檔案;
- owner_pwd,str,比使用者密碼更高一級,提供時可讓打開檔案不受任何限制,不指定時預設owner_pwd 與 user_pwd 相同;
- use_128bit 布爾值,用來表示是否使用128位作為密碼,False 時代表用 40 位密碼,預設為True;
pdf解密
解密是在讀取檔案時用的,用到 decrypt() 函數
rom 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 在文本提取方面要好得多,工欲善其事,必先利其器;在之後的教程中我将會介紹一下這方面的内容,期待大家的關注!
好了以上就是本篇内容的全部内容,最後感謝大家的閱讀,我們下期見~