Portable Document Format(可移植文檔格式)刚照,或者PDF是一種文件格式伐庭,可以用于跨操作系統(tǒng)的呈現(xiàn)和文檔交換衡创。盡管PDF最初是由Adobe發(fā)明的勘高,但它現(xiàn)在是由國(guó)際標(biāo)準(zhǔn)化組織(ISO)維護(hù)的開(kāi)放標(biāo)準(zhǔn)峡蟋。你可以通過(guò)使用PyPDF2包在Python中處理已先存在的PDF。
PyPDF2
是一個(gè)純Python包华望,可用于許多不同類型的PDF操作蕊蝗。
本文將帶你了解如何執(zhí)行以下操作:
從Python中提取PDF中的文檔信息
旋轉(zhuǎn)頁(yè)面
合并PDF
拆分PDF
添加水印
加密PDF
pyPdf,PyPDF2和PyPDF4的歷史
最初的pyPdf軟件包于2005年發(fā)布赖舟。pyPdf的最后一個(gè)正式版本是在2010年蓬戚。大約一年后,一家名為Phasit的公司贊助了一個(gè)名為PyPDF2的pyPdf分支宾抓。該代碼編寫(xiě)為向后與原始代碼兼容子漩,并且用了好多年,效果一直很好石洗,其最后一個(gè)版本是在2016年幢泼。
有一個(gè)名為PyPDF3的軟件包簡(jiǎn)短系列版本,然后該項(xiàng)目被重命名為PyPDF4讲衫。所有這些項(xiàng)目都完全相同缕棵,但pyPdf和PyPDF2 +之間的最大區(qū)別在于后者版本增加了Python 3支持。Python 3的原始pyPdf有一個(gè)不同的Python 3分支,但是這個(gè)分支已經(jīng)多年沒(méi)有維護(hù)了招驴。
雖然最近放棄了PyPDF2篙程,但新的PyPDF4與PyPDF2沒(méi)有完全的向后兼容性。本文中的大多數(shù)示例都可以與PyPDF4完美配合别厘,但也有一些不能虱饿,這就是為什么PyPDF4在本文中沒(méi)有更多的特色。隨意用PyPDF4替換PyPDF2的導(dǎo)入触趴,看看它是如何工作的氮发。
pdfrw:一個(gè)替代的PDF操作包
Patrick Maupin創(chuàng)建了一個(gè)名為pdfrw的軟件包,它可以完成許多與PyPDF2相同的工作雕蔽。除了加密的特殊情況外折柠,本文后面提到PyPDF2的所有操作,pdfrw均可以實(shí)現(xiàn)批狐。
pdfrw的最大區(qū)別在于它與ReportLab軟件包集成扇售,因此你可以使用一些或所有預(yù)先存在的PDF構(gòu)建一個(gè)新的PDF。
PyPDF2的安裝
如果使用Anaconda而不是常規(guī)Python嚣艇,可以使用pip或conda安裝PyPDF2承冰。以下是使用pip安裝PyPDF2的方法:
$ pip install pypdf2
由于PyPDF2沒(méi)有任何依賴,因此安裝非呈沉悖快困乒。
如何從Python****中提取PDF文檔信息
我們可以使用PyPDF2從PDF中提取元數(shù)據(jù)和一些文本,尤其是當(dāng)在預(yù)先存在的PDF文件上執(zhí)行某些類型的自動(dòng)化時(shí)是非常有用的贰谣。
以下是當(dāng)前可以提取的數(shù)據(jù)類型:
Author
Creator
Producer
Subject
Title
Number of page
可以在自己的電腦上隨便找一個(gè)PDF文件進(jìn)行嘗試操作娜搂。下面是使用該P(yáng)DF編寫(xiě)一些代碼,并了解如何訪問(wèn)這些屬性:
from PyPDF2 import PdfFileReader
def extract_information(pdf_path):
with open(pdf_path, 'rb') as f:
pdf = PdfFileReader(f)
information = pdf.getDocumentInfo()
number_of_pages = pdf.getNumPages()
txt = f"""
Information about {pdf_path}:
Author: {information.author}
Creator: {information.creator}
Producer: {information.producer}
Subject: {information.subject}
Title: {information.title}
Number of pages: {number_of_pages}
"""
print(txt)
return information
if __name__ == '__main__':
path = 'xxxx.pdf'
extract_information(path)
首先從PyPDF2包導(dǎo)入PdfFileReader吱抚。PdfFileReader是一個(gè)具有多種與PDF文件交互的方法的類百宇。在此示例中,我們調(diào)用了.getDocumentInfo()秘豹,它將返回DocumentInformation的實(shí)例,包含了我們感興趣的大部分信息携御。我們還可以在reader對(duì)象上調(diào)用.getNumPages(),讓它返回文檔中的頁(yè)數(shù)既绕。
information這個(gè)變量具有多個(gè)實(shí)例屬性啄刹,可以使用這些屬性從文檔中獲取所需的其余元數(shù)據(jù)。我們可以打印出該信息并將其返回以備將來(lái)使用凄贩。
雖然PyPDF2具有.extractText()誓军,可以在其頁(yè)面對(duì)象上使用提取文本(本例中未顯示),但它的效果不是很好疲扎。有些PDF會(huì)返回文本昵时,有些會(huì)返回空字符串廓译。如果要從PDF中提取文本,建議應(yīng)該看一下PDFMiner項(xiàng)目债查。PDFMiner更加強(qiáng)大,專門(mén)用于從PDF中提取文本瓜挽。
如何旋轉(zhuǎn)頁(yè)面盹廷?
有時(shí)候PDF是橫向模式而不是縱向模式,甚至是顛倒的久橙。當(dāng)有人掃描文檔為PDF或電子郵件時(shí)俄占,很可能會(huì)發(fā)生這種情況。我們可以打印出文檔并閱讀紙質(zhì)版本淆衷,也可以使用Python的強(qiáng)大功能來(lái)旋轉(zhuǎn)有問(wèn)題的頁(yè)面缸榄。
下面看一下如何使用PyPDF2旋轉(zhuǎn)文章的一些頁(yè)面:
def rotate_pages(pdf_path):
pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(path)
# 順時(shí)針旋轉(zhuǎn)90度
page_1 = pdf_reader.getPage(0).rotateClockwise(90)
pdf_writer.addPage(page_1)
# 逆時(shí)針旋轉(zhuǎn)90度
page_2 = pdf_reader.getPage(1).rotateCounterClockwise(90)
pdf_writer.addPage(page_2)
# 在正常方向上添加一頁(yè)
pdf_writer.addPage(pdf_reader.getPage(2))
with open('rotate_pages.pdf', 'wb') as fh:
pdf_writer.write(fh)
if __name__ == '__main__':
path = '新路徑.pdf'
rotate_pages(path)
上面除了pdfileReader之外,還導(dǎo)入了pdfileWriter祝拯,因?yàn)槲覀冃枰帉?xiě)一個(gè)新的pdf甚带。rotate_pages()獲取要修改的PDF的路徑。在這個(gè)函數(shù)中佳头,需要?jiǎng)?chuàng)建一個(gè)可以命名為pdf-writer的writer對(duì)象和一個(gè)名為pdf-reader的reader對(duì)象鹰贵。
接下來(lái),可以使用.get page()獲取所需的頁(yè)面康嘉。上面開(kāi)始輸入了第0頁(yè)碉输,也就是第一頁(yè),調(diào)用page對(duì)象的.rotateClockwise()順時(shí)針旋轉(zhuǎn)方法并輸入90亭珍。然后同樣地敷钾,對(duì)于第二頁(yè),調(diào)用.rotateCounterLockwise()逆時(shí)針旋轉(zhuǎn)并輸入90肄梨。
每次調(diào)用Rotation旋轉(zhuǎn)方法后阻荒,都會(huì)調(diào)用.addPage(),這將向writer對(duì)象添加頁(yè)面的旋轉(zhuǎn)版本峭范。最后一頁(yè)是第3頁(yè)财松,沒(méi)有對(duì)其進(jìn)行任何旋轉(zhuǎn)。最后纱控,使用.write()把所有新頁(yè)寫(xiě)入新的PDF辆毡。
如何合并PDF?
在許多情況下甜害,我們希望將兩個(gè)或多個(gè)PDF合并到一個(gè)PDF中舶掖。例如,現(xiàn)在可能有一個(gè)標(biāo)準(zhǔn)的封面尔店,需要轉(zhuǎn)到許多類型的報(bào)告中眨攘。這時(shí)候就可以使用python來(lái)幫助完成這類工作主慰。
下面是實(shí)現(xiàn)的代碼,完成PDF合并的操作:
from PyPDF2 import PdfFileReader, PdfFileWriter
def merge_pdfs(paths, output):
pdf_writer = PdfFileWriter()
for path in paths:
pdf_reader = PdfFileReader(path)
for page in range(pdf_reader.getNumPages()):
# 將每頁(yè)添加到writer對(duì)象
pdf_writer.addPage(pdf_reader.getPage(page))
# 寫(xiě)入合并的pdf
with open(output, 'wb') as out:
pdf_writer.write(out)
if __name__ == '__main__':
paths = ['document1.pdf', 'document2.pdf']
merge_pdfs(paths, output='merged.pdf')
假如有一個(gè)要合并到一起的pdf列表時(shí)鲫售,可以直接使用merge_pdf函數(shù)完成共螺。此函數(shù)采用了輸入路徑和輸出路徑作為參數(shù)。
首先遍歷輸入的paths情竹,并為每個(gè)輸入創(chuàng)建一個(gè)PDF閱讀對(duì)象藐不。然后遍歷PDF文件中的所有頁(yè)面,并使用.addpage()將這些頁(yè)面寫(xiě)入writer對(duì)象秦效。當(dāng)完成對(duì)列表中所有PDF的所有頁(yè)面的寫(xiě)入后雏蛮,將在末尾寫(xiě)入新的結(jié)果中。
如果不想合并每個(gè)PDF的所有頁(yè)面阱州,可以通過(guò)添加一系列要添加的頁(yè)面來(lái)稍微增強(qiáng)這個(gè)腳本挑秉。挑戰(zhàn)一點(diǎn)的話,也可以使用Python的argparse模塊為這個(gè)函數(shù)創(chuàng)建一個(gè)命令行接口苔货。
如何拆分PDF?
有時(shí)可能需要將PDF拆分為多個(gè)PDF犀概,對(duì)于包含大量掃描內(nèi)容的PDF來(lái)說(shuō)尤其重要。以下是如何使用PyPDF2將PDF拆分為多個(gè)文件:
from PyPDF2 import PdfFileReader, PdfFileWriter
def split(path, name_of_split):
pdf = PdfFileReader(path)
for page in range(pdf.getNumPages()):
pdf_writer = PdfFileWriter()
pdf_writer.addPage(pdf.getPage(page))
output = f'{name_of_split}{page}.pdf'
with open(output, 'wb') as output_pdf:
pdf_writer.write(output_pdf)
if __name__ == '__main__':
path = 'xxx.pdf'
split(path, 'jupyter_page')
這個(gè)函數(shù)中再次創(chuàng)建了PDF的reaer對(duì)象夜惭,并對(duì)其所讀取的頁(yè)面進(jìn)行遍歷阱冶。對(duì)于PDF中的每個(gè)頁(yè)面,創(chuàng)建一個(gè)新的PDF的writer實(shí)例并向其添加單個(gè)頁(yè)面滥嘴。然后木蹬,將該頁(yè)面寫(xiě)入一個(gè)唯一命名的文件。腳本運(yùn)行完畢后若皱,就可以將原始PDF的每個(gè)頁(yè)面拆分為單獨(dú)的PDF镊叁。
如何添加水印走触?
水印是紙質(zhì)或者電子文檔上的圖像或圖案晦譬,一些水印只能在特殊照明條件下才能看到。水印的重要性在于它可以保護(hù)你的知識(shí)產(chǎn)權(quán)互广,例如圖像或PDF敛腌。
我們可以使用Python和PyPDF2為文檔添加水印,而且是擁有僅包含水印圖像或文本的PDF惫皱。下面是向PDF添加水印方法:
from PyPDF2 import PdfFileWriter, PdfFileReader
def create_watermark(input_pdf, output, watermark):
watermark_obj = PdfFileReader(watermark)
watermark_page = watermark_obj.getPage(0)
pdf_reader = PdfFileReader(input_pdf)
pdf_writer = PdfFileWriter()
# 給所有頁(yè)面添加水印
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)
if __name__ == '__main__':
create_watermark(
input_pdf='Jupyter_Notebook_An_Introduction.pdf',
output='watermarked_notebook.pdf',
watermark='watermark.pdf')
上面create_watermark有三個(gè)參數(shù):
input_pdf:要加水印的PDF文件路徑
output:要保存PDF的水印版本的路徑
watermark:包含水印圖像或文本的PDF
在代碼中像樊,打開(kāi)水印PDF并從文檔中抓取第一頁(yè),因?yàn)檫@是水印應(yīng)該駐留的位置旅敷。然后使用input_pdf和通用pdf_writer對(duì)象創(chuàng)建PDF的writer對(duì)象生棍,以寫(xiě)出帶水印的PDF。
下一步是遍歷input_pdf中的頁(yè)面媳谁,然后調(diào)用.mergePage()并以用上面讀取的水印對(duì)象watermark_page為參數(shù)涂滴,這樣會(huì)將watermark_page覆蓋在當(dāng)前頁(yè)面的頂部友酱,然后再將新合并的頁(yè)面添加到pdf_writer對(duì)象中。遍歷完成后柔纵,最后將新加水印的PDF寫(xiě)入磁盤(pán)缔杉。
如何加密PDF?
PyPDF2目前僅支持將用戶密碼和所有者密碼添加到預(yù)先存在的PDF搁料。在PDF版本中壮吩,所有者密碼會(huì)提供PDF的管理員權(quán)限,并允許設(shè)置文檔的權(quán)限加缘,而用戶密碼只允許打開(kāi)文檔。
實(shí)際上觉啊,PyPDF2是不允許設(shè)置文檔的任何權(quán)限的拣宏,即使它允許設(shè)置所有者密碼的情況下。但無(wú)論如何杠人,這是可以加密的方式勋乾,也將固有地加密PDF:
from PyPDF2 import PdfFileWriter, PdfFileReader
def add_encryption(input_pdf, output_pdf, password):
pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(input_pdf)
for page in range(pdf_reader.getNumPages()):
pdf_writer.addPage(pdf_reader.getPage(page))
pdf_writer.encrypt(user_pwd=password, owner_pwd=None,
use_128bit=True)
with open(output_pdf, 'wb') as fh:
pdf_writer.write(fh)
if __name__ == '__main__':
add_encryption(input_pdf='reportlab-sample.pdf',
output_pdf='reportlab-encrypted.pdf',
password='twofish')
add_encryption以輸入輸出PDF路徑和要添加到PDF的密碼為參數(shù)。由于需要加密整個(gè)輸入PDF嗡善,因此需要遍歷其所有頁(yè)面并將其添加到writer編寫(xiě)器辑莫。最后一步是調(diào)用.encrypt(),以用戶密碼罩引,所有者密碼以及是否應(yīng)該添加128位加密為參數(shù)各吨。默認(rèn)情況下,要啟用128位加密袁铐。如果將其設(shè)置為False揭蜒,則將應(yīng)用40位加密。
結(jié)論
PyPDF2包非常有用剔桨,可以使用PyPDF2自動(dòng)執(zhí)行腳本完成PDF文檔的批量操作屉更。本文介紹了如何從PDF中提取元數(shù)據(jù),旋轉(zhuǎn)頁(yè)面洒缀,合并和拆分PDF瑰谜,添加水印,以及添加加密的操作树绩。
同時(shí)萨脑,還要關(guān)注較新的PyPDF4包,因?yàn)樗芸炀蜁?huì)取代PyPDF2饺饭。也可以看看pdfrw包砚哗,它也可以執(zhí)行許多與PyPDF2相同的操作。