帶你用Python玩轉PDF的九大技巧

帶你用Python玩轉PDF的九大技巧

儘管PDF最開始是由Adobe發明的,但它現在已經成為國際標準組織ISO維護的公開標準了。大家可以在Python中通過PyPDF2包來處理已存在的PDF。PyPDF2是一個純Python的包,通過它可以進行多種不同類型的PDF操作。通過閱讀本文,您將瞭解以下技能:

提取PDF信息

旋轉PDF頁面

合併PDF

拆分PDF

添加水印

加密PDF

目錄

· PyPdf、PyPDF2、PyPDF4的發展史

· PDF工具包:pdfrw

· 安裝 PyPDF2

· 提取PDF信息

· 旋轉PDF頁面

· 合併PDF

· 拆分PDF

· 添加水印

· 加密PDF

· 總結

對Python感興趣的小夥伴,記得私信小編“007”領取全套Python資料哦。

01 、PyPdf、PyPDF2、PyPDF4的發展史

最初的pyPdf包是在2005年發行的。最後一個官方pyPdf是在2010年發行的。大約一年後,一家名為Phasit的公司贊助了pyPdf的一個分支PyPDF2。這個包的代碼可以與最初的版本兼容,並且在好幾年內都運行得相當好,直到2016年發行最後一版。

在PyPDF3發佈了幾個版本後,項目被重命名為PyPDF4。雖然這些項目版本幾乎做同樣的事,但pyPdf和PyPDF2+之間最大的不同在於後來的版本支持Python3。雖然Python3也有一個原始pyPdf分支,但並沒有維持多少年。

儘管PyPDF2最近不再使用,新的PyPDF4也無法完全向後兼容PyPDF2。儘管本文的大多數例子使用PyPDF4都運行得非常順利,但其中有一些不行,這正是本文沒有過多強調PyPDF4的原因。當然,您也可以導入PyPDF4來替換PyPDF2,觀察它是如何實現的。

02、PDF工具包:pdfrw

Patrick Maupin 發明了pdfrw包,可以完成很多PyPDF2的工作。您可以使用pdfrw處理本文中用PyPDF2處理的同類型任務,加密任務除外。

pdfrw最大的不同在於整合了ReportLab包,因此您可以使用已存在的PDF,通過ReportLab將這些已存在的PDF的部分或全部創建成新的文檔。

03、安裝PyPDF2

如果您剛好使用Anaconda,而不是純Python,可以用pip或者conda安裝PyPDF2,以下用pip安裝PyPDF2。

pip install pypdf2

PyPDF2的安裝不需要依賴任何包,所以安裝很快,並且下載包的速度和安裝的速度差不多。

04、提取PDF信息

您可以使用PyPDF2從PDF中提取元數據和一些文本。當您在已存在的PDF文件中執行某些類型的自動化時,這會比較有用。

可以提取的數據類型如下:

· 作者

· 創建者

· 生成器

· 主題

· 標題

· 頁數

您需要找到一個PDF來運行這個例子,電腦上的任意PDF皆可。為了簡便,我去Leanpub選取了我一本書中的一個樣本。您可以下載這個樣本—reportlab-sample.pdf.

以下代碼處理PDF,您可以瞭解到這些屬性:

# extract_doc_info.py
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 = 'reportlab-sample.pdf'
extract_information(path)

從PyPDF2包中導入PdfFileReader。PdfFileReader是一個類,包含與PDF文件交互的幾個方法。在此例子中,調用.getDocumentInfo(),返回DocumentInformation的一個實例,包含了您感興趣的大多數信息。您也可以調用對象的.getNumPages(),返回文件的頁數。

可以用Information變量的幾個屬性來獲取文檔中的其餘元數據。可以打印出信息和返回信息以備未來之需。

儘管PyPDF2的

.extractText()可用在頁數對象上(此例子未展示),但並不好用。因為有些PDF會返回文本內容,有些則會返回空字符。當您希望從一篇PDFF中提取文檔,您需要檢查PDFMiner。PDFMiner是專門為從PDF中提取文檔的功能設計的,更為穩定。

【注】最後一段代碼使用Python3最新的字符串格式。如果您想學習更多,可以查閱Python 3’s f-Strings: An Improved String Formatting Syntax (Guide).

05、旋轉PDF頁面

有時候,您會收到一些橫向模式而不是縱向模式的PDF,或者這些文檔是上下顛倒的。如下圖:

帶你用Python玩轉PDF的九大技巧

這種情況經常出現在人們把文件掃描成PDF或者郵件。您可以打印文件,瞭解文章版本,或者使用Python旋轉有問題的頁面。在此例中,您可以去往Real Python article 打印PDF來學習PyPDF2。

以下是用PyPDF2旋轉文章頁面的代碼:

# rotate_pages.py
from PyPDF2 import PdfFileReader, PdfFileWriter
def rotate_pages(pdf_path):
pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(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)
# Add a page in normal orientation
pdf_writer.addPage(pdf_reader.getPage(2))
with open('rotate_pages.pdf', 'wb') as fh:
pdf_writer.write(fh)
if __name__ == '__main__':
path = 'Jupyter_Notebook_An_Introduction.pdf'
rotate_pages(path)
"""
print(txt)
return information
if __name__ == '__main__':
path = 'reportlab-sample.pdf'
extract_information(path)

在此例中,您需要導入PdfFileWriterPdfFileReader,因為您需要輸出新的PDF。rotate_pages()輸入您想要修正的PDF路徑。在這個功能中,您需要創建一個寫對象

pdf_writer,一個讀對象pdf_reader

然後,您可以使用.getPage()獲取想要的頁面。您可以抓取頁面0,即首頁。然後您可以調用頁面對象的.rotateClockwise()方法,輸入90度。同樣的,對於頁面2,您可以調用.rotateCounterClockwise()並輸入90度。

每次調用旋轉方法後,調用.addPage()來添加頁面的旋轉版本到寫對象。添加到寫對象的最後一頁是沒有任何旋轉的頁面3。

最後,用.write()寫入新的PDF,其用file-like object作為參數。新的PDF包含3頁,前兩頁以相反方向旋轉成橫向,第三頁正常。

【注】PyPDF2包只允許您以90度的增量旋轉頁面,如90、180、270……,否則會收到AssertionError報錯。

06、合併PDF

很多情況下需要將兩個或多個PDF合併成一個PDF。比如,有一個標準封面需要加到多種類型的報告中。您可以使用Python幫您處理。

在此例中,您可以打開一個PDF,輸出一個頁面作為獨立的PDF,重複輸出獲得不同頁面,這樣就有兩個輸入作為本例的目的。以下是合併的代碼:

# pdf_merging.py
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()):
# Add each page to the writer object
pdf_writer.addPage(pdf_reader.getPage(page))
# Write out the merged 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')

若想合併一系列PDF,可以調用merge_pdfs(),並且需要知道保存結果的路徑。因此這個函數的輸入參數是這些PDF的路徑以及結果保存的路徑。

然後遍歷原PDF的路徑,併為每個路徑創建一個PDF讀對象。接著,您需要遍歷該路徑對應的PDF所有頁面,通過.addPage()添加到頁面本身。完成所有PDF所有頁面的處理後,即可以進行寫操作,輸出PDF。

需要注意的是,如果您不想合併每個PDF的所有頁面,需要添加一系列要添加的頁面來強化這個腳本。若您願意挑戰,也可以使用Python的

argparse模塊為這功能創建命令行界面。

07、拆分PDF

有時候需要將一個PDF拆分成多個PDF,尤其當PDF包含很多掃描的內容,但是也有眾多其它原因需要去拆分PDF。以下是使用PyPDF2將一個PDF拆分成多個。

# pdf_splitting.py
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 = 'Jupyter_Notebook_An_Introduction.pdf'
split(path, 'jupyter_page')

在此例中,首先創建PDF讀對象,並遍歷頁面。對每個頁面,創建新的PDF寫實例,並添加新的頁面。然後將頁面寫入獨立命名的文件。當腳本結束運行,得到原始PDF的每個頁面拆分成獨立PDF。

08、添加水印

水印可以識別印刷和數字文件的圖片和圖像模式。有些水印只能在特殊光照條件下才能看見。水印非常重要,可以保護知識產權,比如圖像或者PDF。水印的另一術語是疊加。

可以通過Python和PyPDF2為文件添加水印。您的PDF必須只包含水印圖或者文本。以下是添加水印代碼:

# pdf_watermarker.py
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()
# 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)
if __name__ == '__main__':
create_watermark(
input_pdf='Jupyter_Notebook_An_Introduction.pdf',
output='watermarked_notebook.pdf',
watermark='watermark.pdf')

create_watermark()接收三個參數:

input_pdf:需要加水印的PDF路徑

output:水印版本PDF的保存路徑

③ watermark:含有水印圖像或文本的PDF

代碼中,打開水印PDF,從文檔的第一頁抓取信息,因為第一頁即保存了水印內容。然後用

input_pdf創建PDF讀對象並創建通用的pdf_writer對象來寫入加了水印的PDF。

下一步是遍歷input_pdf的所有頁面,魔法開始了。調用.mergePage()並傳入watermark_page。這一步是將水印頁面疊加到現有頁面中,然後將新的疊加頁面添加到pdf_writer對象中。最後,把新的加了水印的PDF寫入到磁盤中,完成。

09、加密PDF

PyPDF2目前只支持對已有PDF添加用戶密碼和所有者密碼。在PDF中,所有者密碼基本上擁有對該PDF的管理員權限,允許對文檔設置權限。另一方面, 用戶密碼僅允許打開文件。

正如所說的,PyPDF2事實上並不能對文檔設置任何權限,即使可以設置所有者密碼。無論如何,以下是添加密碼、本質上加密PDF的代碼:

# pdf_encrypt.py
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的密碼。打開PDF讀和寫對象,遍歷PDF所有頁面並添加到寫對象。最後調用.encrypt(),參數為用戶密碼、所有者密碼以及是否128位加密。默認128位加密,若設置False, 則為40位加密。

【注】根據pdflib.com,PDF加密技術要麼採用RC4,要麼採用AES(Advanced Encryption Standard)。因為加密了PDF並不意味著百分百安全,也有一些工具能從PDF中刪除密碼。若想了解更多,卡內基梅隆大學在這方面有一個有趣的論文【1】。

10、總結

PyPDF2包相當有用,通常情況下也比較快。您可以使用PyPDF2自動化大量的工作,利用其能力幫您更好地工作。

在此教程中,包含了以下內容:

· 從PDF提取元數據

· 旋轉頁面

· 合併和拆分PDF

· 添加水印

· 加密PDF

基於以上基本功能,也可以拓展其它功能,如刪除頁面。代碼如下:

# pdf_delete.py
from PyPDF2 import PdfFileWriter, PdfFileReader
def delete(path, name_of_delete, delete_page):
pdf_reader = PdfFileReader(path)
pdf_writer = PdfFileWriter()
for page in range(0, delete_page):
pdf_writer.addPage(pdf_reader.getPage(page))
for page in range(delete_page+1, pdf_reader.getNumPages()):
pdf_writer.addPage(pdf_reader.getPage(page))
output = f'{name_of_delete}.pdf'
with open(output_pdf, 'wb') as fh:
pdf_writer.write(fh)
if __name__ == '__main__':
path = 'merged.pdf'
delete(path, name_of_delete='new.pdf', delete_page=2)

在此刪除例子中,輸入原PDF路徑和刪除後需要保存的路徑以及刪除頁面(本例為第3頁),然後通過讀對象向寫對象添加除了第三頁之外的所有頁面,然後寫入磁盤保存。若刪除多頁,只要在循環添加頁面時省去那些頁面即可。大家也可以考慮更多的功能。

關注更新的PyPDF4,不久後它將取代PyPDF2。您也可以嘗試pdfrw,實現PyPDF2能做的同樣的任務。


分享到:


相關文章: