如何使用Python玩轉(zhuǎn)PDF各種騷操作

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相同的操作。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末砰奕,一起剝皮案震驚了整個(gè)濱河市蛛芥,隨后出現(xiàn)的幾起案子提鸟,更是在濱河造成了極大的恐慌,老刑警劉巖仅淑,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件称勋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡涯竟,警方通過(guò)查閱死者的電腦和手機(jī)赡鲜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)庐船,“玉大人银酬,你說(shuō)我怎么就攤上這事】鹬樱” “怎么了揩瞪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)篓冲。 經(jīng)常有香客問(wèn)我李破,道長(zhǎng),這世上最難降的妖魔是什么壹将? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任嗤攻,我火速辦了婚禮,結(jié)果婚禮上诽俯,老公的妹妹穿的比我還像新娘妇菱。我一直安慰自己,他們只是感情好暴区,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布恶耽。 她就那樣靜靜地躺著,像睡著了一般颜启。 火紅的嫁衣襯著肌膚如雪偷俭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天缰盏,我揣著相機(jī)與錄音涌萤,去河邊找鬼。 笑死口猜,一個(gè)胖子當(dāng)著我的面吹牛负溪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播济炎,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼川抡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起崖堤,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侍咱,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后密幔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體楔脯,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年胯甩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昧廷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡偎箫,死狀恐怖木柬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淹办,我是刑警寧澤眉枕,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站娇唯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏寂玲。R本人自食惡果不足惜塔插,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拓哟。 院中可真熱鬧想许,春花似錦、人聲如沸断序。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)违诗。三九已至漱凝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诸迟,已是汗流浹背茸炒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阵苇,地道東北人壁公。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绅项,于是被迫代替她去往敵國(guó)和親紊册。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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

  • Portable Document Format(可移植文檔格式)快耿,或者PDF是一種文件格式囊陡,可以用于跨操作系統(tǒng)的...
    諸葛青云999閱讀 4,107評(píng)論 0 5
  • 在五月的光韻里 初遇你的瞬間 讓我驚艷 被花兒的芬芳 迷醉 我才知道 原來(lái) 你的心 依舊為我守候 那旖旎了一季的 ...
    沐雨夕煙閱讀 410評(píng)論 0 8
  • 成都的天空关斜,灰白偶見(jiàn)淺藍(lán)示括,太陽(yáng)卻晃的人睜不開(kāi)眼! 成都的風(fēng)痢畜,偶爾搖動(dòng)樹(shù)枝垛膝,卻不見(jiàn)絲毫的涼爽,炙熱空...
    袁笨笨閱讀 684評(píng)論 1 3
  • 我想,大概每個(gè)人都經(jīng)歷過(guò)一段有緣無(wú)分的感情吧线衫。 有些人和事凿可,已經(jīng)漸漸地被歲月磨平,藏匿在記憶深處授账,清閨夢(mèng)里枯跑。 我們...
    樂(lè)棠閱讀 493評(píng)論 2 0
  • 中國(guó)是文學(xué)大國(guó),散文幾乎是最早的文學(xué)品種白热×仓孔子整理《詩(shī)經(jīng)》時(shí),老子的《道德經(jīng)》早已成熟屋确。其后他們的學(xué)生孟子雄辯滔滔...
    八里山人程遠(yuǎn)河閱讀 540評(píng)論 10 9