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

大家好,我是小張~娃承,今天文章與自動(dòng)化辦公相關(guān)奏夫,目前個(gè)人認(rèn)為 Python 庫(kù)中處理 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 中某頁(yè)進(jìn)行旋轉(zhuǎn) ;

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

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

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

  • 6,獲取 PDF 基本信息蜘渣,例如作者淌铐、標(biāo)題、頁(yè)數(shù)等蔫缸;

PyPDF2 歷史

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

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

PyPDF2 安裝

與其它Python 庫(kù)一樣,安裝可通過(guò) pip 或 conda 工具

pip install pypdf2

PDF 信息提取

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

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

  • 作者吞加;

  • 創(chuàng)建者;

  • 制作者酝惧;

  • Subject昭伸;

  • 標(biāo)題物舒;

  • 頁(yè)數(shù);

這里我下載了官網(wǎng)提供的 PDF 樣本《Seige_of_Vicksburg_Sample_OCR》一共六頁(yè)杨何,作為測(cè)試數(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 方法也可以返回文檔頁(yè)數(shù)悍及;

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

PDF 頁(yè)面旋轉(zhuǎn)

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

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

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

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

下面代碼表示將目標(biāo) PDF 中第一頁(yè)順時(shí)針?lè)较蛐D(zhuǎn) 90 度,第二頁(yè)以逆時(shí)針?lè)较蛐D(zhuǎn) 90 度有咨,其它頁(yè)位置角度不變琐簇;

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)
# 之后的正常寫(xiě)出
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è)類,頁(yè)面旋轉(zhuǎn)并不是在原有 PDF 基礎(chǔ)上進(jìn)行操作而是在內(nèi)存處創(chuàng)建了一個(gè)新的PDF流對(duì)象座享,將操作后的每一頁(yè)通過(guò) addPage() 方法加入到這個(gè)對(duì)象中婉商,之后將內(nèi)存中的這個(gè)對(duì)象寫(xiě)入到文件中似忧;

寫(xiě)到這里,說(shuō)實(shí)話其實(shí) 頁(yè)面旋轉(zhuǎn) 這個(gè)功能沒(méi)基本沒(méi)什么作用据某,加在這里只是想充當(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 原文件中的每一頁(yè)拆分到每一個(gè)PDF文件,其中文件名用頁(yè)索引來(lái)命名癣籽;

image-20210313235957539

通過(guò)拆分也可以提取到 pdf 文件中固定頁(yè)碼范圍挽唉,例如我只想提取 pdf 中的 2-5 頁(yè),其它部分不要筷狼,那么代碼將寫(xiě)成下面形式

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埂材,并遞歸獲取每一頁(yè)page 對(duì)象塑顺, PdfFileWrite 新建一個(gè)流對(duì)象,把前面內(nèi)存中讀取到的 page 對(duì)象按順序?qū)懭氲竭@個(gè)流對(duì)象中俏险,最后寫(xiě)入到磁盤(pán)文件

···
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() 方法竖独,通過(guò)將兩個(gè)頁(yè)面合并來(lái)達(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í)沒(méi)有考慮到頁(yè)面布局問(wèn)題刊侯,所以合并時(shí)出現(xiàn)一部分缺失;

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

PDF加密解密

pdf加密

對(duì)一份 pdf 文件疮绷,如果我們不想讓其他人能夠讀取里面的內(nèi)容,可以通過(guò) 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,用戶密碼喜德,用來(lái)限制打開(kāi)讀取文件山橄;

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

  • use_128bit 布爾值饮醇,用來(lái)表示是否使用128位作為密碼,F(xiàn)alse 時(shí)代表用 40 位密碼秕豫,默認(rèn)為T(mén)rue朴艰;

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)

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

小結(jié)

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

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

好了以上就是本篇內(nèi)容的全部?jī)?nèi)容榕酒,最后感謝大家的閱讀胚膊,我們下期見(jiàn)~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市想鹰,隨后出現(xiàn)的幾起案子紊婉,更是在濱河造成了極大的恐慌,老刑警劉巖辑舷,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喻犁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)肢础,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)还栓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人传轰,你說(shuō)我怎么就攤上這事剩盒。” “怎么了慨蛙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵辽聊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我期贫,道長(zhǎng)身隐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任唯灵,我火速辦了婚禮贾铝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘埠帕。我一直安慰自己垢揩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布敛瓷。 她就那樣靜靜地躺著叁巨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呐籽。 梳的紋絲不亂的頭發(fā)上锋勺,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音狡蝶,去河邊找鬼庶橱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贪惹,可吹牛的內(nèi)容都是我干的苏章。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奏瞬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枫绅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起硼端,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤并淋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后珍昨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體县耽,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡订咸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酬诀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脏嚷。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞒御,靈堂內(nèi)的尸體忽然破棺而出父叙,到底是詐尸還是另有隱情,我是刑警寧澤肴裙,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布趾唱,位于F島的核電站,受9級(jí)特大地震影響蜻懦,放射性物質(zhì)發(fā)生泄漏甜癞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一宛乃、第九天 我趴在偏房一處隱蔽的房頂上張望悠咱。 院中可真熱鬧,春花似錦征炼、人聲如沸析既。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)眼坏。三九已至,卻和暖如春酸些,著一層夾襖步出監(jiān)牢的瞬間宰译,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工魄懂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沿侈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓逢渔,卻偏偏與公主長(zhǎng)得像肋坚,于是被迫代替她去往敵國(guó)和親乡括。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肃廓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容