python處理操作pdf全攻略

王平發表於2019-04-29

本篇聊下Python對pdf的各種操作,包含pdf轉word,pdf轉圖片,pdf翻轉,加密,加水印等。

python處理pdf

pdf轉換word文件 保留格式

pdf轉換為word文件,被大眾經常使用的是純Python庫pdfminer和python-docx搭配使用,不過pdfminer轉換成word,會丟失原來的pdf格式(圖片和樣式會丟失),只能是一個純文字的。

比如下面是一個pdf文件,是一個目錄索引樣式。

格式完好的文件

使用pdfminer和python-docx轉換的話樣式會丟失,如下。

pdfminer轉換後格式混亂的文件

為了研究怎麼保留樣式,我花了好些時間,最終測試驗證了一種能接受的方案:使用libreoffice

libreoffice是一個免費的辦公軟體,能開啟和操作docx,ppt,pdf等,提供不同文件格式之間的轉換,而且支援命令列。

支援命令列的話,我們就能透過python的os.system()等方法呼叫libreoffice軟體去做文件轉換工作了。

首先要安裝libreoffice:

下載地址如下:

https://zh-cn.libreoffice.org/download/libreoffice-still/

windows,linux,max三種版本都有。

安裝成功後,在libreoffice/program 目錄下面有個soffice.exe命令,我們就是用python呼叫soffice來做pdf和word轉換。來測試一下pdf轉word功能。


import os
os.system('D:\Program Files\libreoffice\program\soffice --infilter=writer_pdf_import --convert-to docx D:\code\pdf\ss.pdf --outdir D:\code\pdf')

上面的命令是把ss.pdf 轉換成docx格式,儲存在D:\code\pdf\ 目錄裡,檔名是跟pdf同名,只是檔案會變成.docx 。

來開啟轉換後的docx文件看一下,樣式保留得還可以,要用office2007以上版本開啟,office2003的開啟樣式有問題。

soffice轉換pdf到word格式保留

libreoffice轉換的缺點是,不報錯,你不知道是否轉換成功。還有表格和多圖的轉換還有瑕疵。轉換耗時會隨著文件頁數快速增加。

 

pdf轉word文件 不保留格式

不保留格式,只需要文字的話,就直接使用 pdfminer和python-docx兩個庫搭配就好。pdfminer把pdf裡的文字內容抽取出來,python-docx負責把抽取出來的寫進word文件裡。


from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfinterp import process_pdf
from io import StringIOf
from docx import Document

def remove_characters(content):
    mpa = dict.fromkeys(range(32))
    return content.translate(mpa)
    
def convert_pdf_to_txt(path):
    with open(path,'rb') as f:
        rsrcmgr = PDFResourceManager()
        retstr = StringIO()
        laparams = LAParams()
        device = TextConverter(rsrcmgr, retstr, laparams=laparams)
        
        process_pdf(rsrcmgr, device, f)
        text = retstr.getvalue()
        device.close()
        retstr.close()
        return text
        
def convert_txt_to_doc(text, doc_path):
    doc = Document()
    for line in text.split('\n'):
        paragraph = doc.add_paragraph()
        paragraph.add_run(remove_characters(line))
    doc.save(doc_path)

def convert():
    text = convert_pdf_to_txt('d:/sphinx_doc_zhcn.pdf')
    convert_txt_to_doc(text, 'd:/test.docx')

convert()

寫進word文件之前要把內容split分行,不然內容全都在一行,沒有段落。還有要刪掉一些亂七八糟的控制字元,不然儲存word文件的時候要報錯。

當然你需要使用pip install pdfminer 和 python-docx 這兩個庫。

 

pdf轉圖片

把pdf轉換成圖片的方案很多,比如wand庫、PythonMagick等都能實現,pdf轉圖片的方案選擇主要是考慮轉換效能和圖片質量上。轉換一個幾百K的pdf文件分辨不出哪個好使,如果是批次轉換成百上千個pdf文件,又或者是轉換一個幾十M大小的pdf文件時就有優劣。

如果想程式簡單和轉換後的圖片質量還OK,可使用pdf2image + Poppler 方案。Poppler 是一個處理pdf文件很有用的庫,支援windows/linux/mac 。pdf2image是一個wrapper,提供呼叫Poppler的python介面。

首先要下載Poppler

https://blog.alivate.com.au/poppler-windows/

windows使用者下載帶x86字樣的。

下載解壓後,要把poppler下的bin目錄絕對路勁加入到系統環境變數裡,

比如我的是 D:\Program Files\poppler-0.68.0\bin ,要加入到系統環境變數Path變數裡。不然pdf2image不知道去哪兒呼叫poppler。

然後安裝pdf2image

pip install pdf2image

最後呼叫執行


from pdf2image import convert_from_path

pages = convert_from_path('D:/test/18.pdf')
for i, page in enumerate(pages):
    page.save('D:/test/18_{}.jpg'.format(str(i)), 'JPEG')

不過這種方法要處理十幾M以上的pdf時,我親測慢如蝸牛,記憶體還可能不夠。處理大檔案可以使用ghostscript,也是一個c程式,速度還過得去,我用它一次批處理數千個十幾M pdf文件也花了好幾個小時。這裡就不再贅述如何使用ghostscript了。

讀取pdf文件元資訊

比如獲取文件作者,主題,創作時間,文件頁數等等。

pdf文件元資訊

這裡使用經典的PyPDF2庫來操作,下面的操作都PyPDF2完成,當然你需要先安裝,

pip install PyPDF2


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

extract_information('D:/download/zj/2018.pdf')

 

翻轉pdf

對pdf文件向左向右翻轉90度,並儲存為一個新的文件。

翻轉pdf前的樣子

90度翻轉pdf文件後的樣式


from PyPDF2 import PdfFileReader, PdfFileWriter

def rotate_pages(pdf_path):
    # 新建一個空白pdf    
    pdf_writer = PdfFileWriter()
    
    # 讀取要翻轉的pdf
    pdf_reader = PdfFileReader(path)
    
    # 把pdf第一頁向右翻轉90度並寫入新建的空白pdf裡    
    page_1 = pdf_reader.getPage(0).rotateClockwise(90)    
    pdf_writer.addPage(page_1)
    
    # 把pdf第二頁向左翻轉90度並寫入新建的空白pdf裡   
     page_2 = pdf_reader.getPage(1).rotateCounterClockwise(90)    
     pdf_writer.addPage(page_2)
     
     # 把pdf第三頁不翻轉正常寫入    
     pdf_writer.addPage(pdf_reader.getPage(2))
     
     # 把新建的pdf文件儲存到本地
     with open('D:/download/rotate_pages.pdf', 'wb') as fh:
         pdf_writer.write(fh)
 
rotate_pages('D:/download/zj/18.pdf')

從上面讀取pdf文件元資訊和翻轉pdf可以看出,操作PyPDF2來讀取和寫入pdf的方法分別是PdfFileReader和PdfFileWriter

 

合併pdf文件

把多個pdf文件合併成一個,操作方法也很簡單,仍然是先使用PdfFileReader方法讀取pdf每一個頁面,然後用PdfFileWriter寫入一個到新的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()): 
            pdf_writer.addPage(pdf_reader.getPage(page))
    
    with open(output, 'wb') as out:
        pdf_writer.write(out)
 
paths = ['d:/test/1.pdf', 'd:/test/2.pdf']
merge_pdfs(paths, output='d:/test/merged.pdf')

 

拆分pdf分檔

把一個pdf文件拆分成多個,操作思路都差不多,先用PdfFileReader讀取pdf文件,再用PdfFileWriter把每一頁寫入新的。

下面的例子是把一個pdf文件每一頁都拆分成一個pdf文件,按檔案頁數來命令新的文件。


from PyPDF2 import PdfFileReader, PdfFileWriter
def split(path):
    pdf = PdfFileReader(path)
    for page in range(pdf.getNumPages()):
        pdf_writer = PdfFileWriter()
        pdf_writer.addPage(pdf.getPage(page))  
              
        output = f'{page}.pdf'
        with open(output, 'wb') as output_pdf:
            pdf_writer.write(output_pdf)
split('d:/test/test.pdf')

 

給pdf文件新增水映

可以呼叫mergePage()方法給pdf文件加水印,PyPDF2的操作方法是把一個水印pdf文件(這個pdf文件裡只有水印,水印可以是文字或圖片),重疊到待加水印的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()    
    
    # 給每一個頁面都加上水印
    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)

create_watermark(input_pdf='D:/code/pdf/sphinx.pdf', 
                output='D:/code/pdf/watermarked_sphinx.pdf',
                watermark='D:/download/zj/watermark.pdf')

如下,我有一個水印文件watermark.pdf,裡面只有猿人學三個字。

猿人學水印

把它加入到sphinx.pdf的效果是:

python給pdf文件加水印的效果

PS:要保持加水印後的文件視覺效果好看,需要對水印做一些處理的,比如對水印文字或圖片要透明處理,不然文件沒法看。

 

給pdf文件加密

給你一個pdf文件加密也很簡單,呼叫encrypt()方法即可。


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)

add_encryption(input_pdf='no_password.pdf',
               output_pdf='password.pdf',
               password='yuanrenxue')

加密之後,如何解密開啟呢?


from PyPDF2 import PdfFileReader

pdfFile = open('password.pdf','rb')
pdfReader = PdfFileReader(pdfFile) 

#為True表示是加密文件 yuanrenxue 是密碼
if pdfReader.isEncrypted:   
    pdfReader.decrypt('yuanrenxue')

上面可以看出,PyPDF2庫是不能對pdf文字內容進行寫操作,只能對pdf文件進行讀取,頁面複製,加解密,新建pdf文件操作。

 

Done ,終於說完了,寫python教程技術類的文章挺繁瑣的,程式要除錯,還要截圖,花了數小時,還是寫雞湯文快速。

 

猿人學banner宣傳圖

我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。

***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***

相關文章