pdf各种处理 PDF 的实用代码:PyPDF2、PDFMiner、pdfplumber
你不懂得安排自己的人生,會有很多人幫你安排,他們需要你做的事。
PDF文件我們經(jīng)常用,尤其是這兩個場景:
-
下載參考資料,如各類報告、文檔
分享只讀資料,方便傳播同時保留源文件
場景和模塊
所以,對于PDF文件,常見的需求也就是兩類:
處理文件本身,屬于文件頁面級操作,如合并/分拆PDF頁面、加/解密、加/去水印;
處理文件內(nèi)容,屬于內(nèi)容級操作,如提取文字、表格數(shù)據(jù)、圖表等。
目前Python用于處理PDF的模塊,主要有3個:
-
PyPDF2:模塊成熟,最后一次更新在2年前,適合頁面級操作,文字提取效果較差。
PDFMiner:擅長文字抽取,目前主分支已停止維護(hù),取而代之的是pdfminer.six
pdfplumber:基于pdfminer.six的文本內(nèi)容抽取工具,使用門檻更低,如支持表格提取。
實戰(zhàn)中,可以根據(jù)需求的類型選擇模塊。如果是頁面級的操作,就用PyPDF2,如果需要內(nèi)容抽取,優(yōu)先使用pdfplumber。
對應(yīng)的模塊安裝:
pip install pypdf2pip install pdfminer.sixpip install pdfplumber下面按使用場景演示3個模塊的使用。
PyPDF2
PyPDF2的主要能力在頁面級操作,比如:
-
獲取PDF文檔基本信息
PDF分割及合并
PDF的旋轉(zhuǎn)及排序
PDF加水印及去水印
PDF加密及解密
PyPDF2的核心兩個類是PdfFileReader和PdfFileWriter,完成PDF文件的讀寫操作。
獲取PDF文檔基本信息
import pathlib from PyPDF2 import PdfFileReader path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') with open(f_path, 'rb') as f:pdf = PdfFileReader(f)info = pdf.getDocumentInfo()cnt_page = pdf.getNumPages()is_encrypt = pdf.getIsEncrypted() print(f''' 作者: {info.author} 創(chuàng)建者: {info.creator} 制作者: {info.producer} 主題: {info.subject} 標(biāo)題: {info.title} 總頁數(shù): {cnt_page} 是否加密: {is_encrypt} ''') PDF分割及合并
import pathlib from PyPDF2 import PdfFileReader, PdfFileWriter path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') out_path = path.joinpath('002pdf_split_merge.pdf') out_path_1 = path.joinpath('002pdf_split_half_front.pdf') out_path_2 = path.joinpath('002pdf_split_half_back.pdf') # 把文件分為兩半 with open(f_path, 'rb') as f, open(out_path_1, 'wb') as f_out1, open(out_path_2, 'wb') as f_out2:pdf = PdfFileReader(f)pdf_out1 = PdfFileWriter()pdf_out2 = PdfFileWriter()cnt_pages = pdf.getNumPages()print(f'共 {cnt_pages} 頁')for i in range(cnt_pages):if i <= cnt_pages //2:pdf_out1.addPage(pdf.getPage(i))else:pdf_out2.addPage(pdf.getPage(i))pdf_out1.write(f_out1)pdf_out2.write(f_out2) # 再把后半個文件與前半個文件合并,后半個文件在前 with open(out_path, 'wb') as f_out:cnt_f, cnt_b = pdf_out1.getNumPages(), pdf_out2.getNumPages()pdf_out = PdfFileWriter()for i in range(cnt_b):pdf_out.addPage(pdf_out2.getPage(i))for i in range(cnt_f):pdf_out.addPage(pdf_out1.getPage(i))pdf_out.write(f_out) PDF的旋轉(zhuǎn)及排序
import pathlib from PyPDF2 import PdfFileReader, PdfFileWriter path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') out_path = path.joinpath('002pdf_rotate.pdf') with open(f_path, 'rb') as f, open(out_path, 'wb') as f_out:pdf = PdfFileReader(f)pdf_out = PdfFileWriter()page = pdf.getPage(0).rotateClockwise(90)pdf_out.addPage(page)# 把第二頁放到前面pdf_out.addPage(pdf.getPage(2))page = pdf.getPage(1).rotateCounterClockwise(90)pdf_out.addPage(page)pdf_out.write(f_out) PDF加水印及去水印
加圖片水印,其實就是在頁面中增加一個透明背景的圖片,通過頁面的mergePage方法即可完成。
import pathlib from PyPDF2 import PdfFileReader, PdfFileWriter path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') wm_path = path.joinpath('watermark.pdf') en_path = path.joinpath('002pdf_with_watermark_en.pdf') out_path = path.joinpath('002pdf_with_watermark.pdf') with open(f_path, 'rb') as f, open(wm_path, 'rb') as f_wm, open(out_path, 'wb') as f_out:pdf = PdfFileReader(f)pdf_wm = PdfFileReader(f_wm)pdf_out = PdfFileWriter()wm_cn_page = pdf_wm.getPage(0)wm_en_page = pdf_wm.getPage(1)cnt_pages = pdf.getNumPages()for i in range(cnt_pages):page = pdf.getPage(i)page.mergePage(wm_cn_page)pdf_out.addPage(page)pdf_out.write(f_out) 去水印,就比較復(fù)雜,需要根據(jù)不同情況具體分析。因為水印可能是文字、圖片或者各種組合,關(guān)鍵是識別出特征。
去水印的3個常見思路參考:
-
找到特征詞后替換,適合英文文檔,但不適用于中文等CJK字符。
把PDF頁轉(zhuǎn)成圖片后,用圖像算法去水印,但這樣會破壞文件原信息結(jié)構(gòu)。
根據(jù)水印大小位置特征,找到所有元素后刪除。這是更推薦的方式。
第3種方式效果最好,但如果碰到一些復(fù)雜的文檔水印,就非常考驗?zāi)托摹?/p>
你得一個個識別操作命令,一邊替換一邊檢查效果,直到水印成功去除。
但,未必剩下的所有頁都可以用同樣特征模式來消除,因為這份PDF可能經(jīng)過多人加水印,已經(jīng)包含多種加水印方式。
所以,去水印并沒有一種100%安全有效(不錯刪信息)且通用的方法。
加水印、去水印本質(zhì)上是一種攻防策略。
比如一些工具推出去水印功能,一旦公開,加水印方就能識別并避開它的去除方法。
最后,尊重版權(quán),是每個人應(yīng)有的態(tài)度。
除了學(xué)習(xí)外,正式使用時,應(yīng)該遵守內(nèi)容創(chuàng)作方的規(guī)則。
PDF加密解密
PDF里的密碼,分為用戶密碼和所有者密碼。
PyPDF2里提供了基本的加密功能,“防君子不防小人”。
如果打開PDF文件后,復(fù)制了新文件,那新文件就不受所有者密碼的約束,可被修改。
import pathlib from PyPDF2 import PdfFileReader, PdfFileWriter path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') out_path_encrypt = path.joinpath('002pdf_encrypt.pdf') out_path_decrypt = path.joinpath('002pdf_decrypt.pdf') with open(f_path, 'rb') as f, open(out_path_encrypt, 'wb') as f_out:pdf = PdfFileReader(f)pdf_out = PdfFileWriter()cnt_pages = pdf.getNumPages()for i in range(cnt_pages):page = pdf.getPage(i)pdf_out.addPage(page)pdf_out.encrypt('123456', owner_pwd='654321')pdf_out.write(f_out) # 重新讀取加密文件并生成解密文件 with open(out_path_encrypt, 'rb') as f, open(out_path_decrypt, 'wb') as f_out:pdf = PdfFileReader(f)if not pdf.isEncrypted:print('文件未被加密')else:success = pdf.decrypt('123456')# if not success:pdf_out = PdfFileWriter()pdf_out.appendPagesFromReader(pdf)pdf_out.write(f_out) pdfminer.six
PDFMiner的操作門檻比較高,需要部分了解PDF的文檔結(jié)構(gòu)模型,適合定制開發(fā)復(fù)雜的內(nèi)容處理工具。
平時直接用PDFMiner比較少,這里只演示基本的文檔內(nèi)容操作:
import pathlib from pdfminer.pdfparser import PDFParser from pdfminer.pdfdocument import PDFDocument from pdfminer.pdfpage import PDFPage from pdfminer.pdfinterp import PDFResourceManager from pdfminer.pdfinterp import PDFPageInterpreter from pdfminer.pdfdevice import PDFDevice from pdfminer.layout import LAParams, LTTextBox, LTFigure, LTImage from pdfminer.converter import PDFPageAggregator path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') with open(f_path, 'rb') as f:parser = PDFParser(f)doc = PDFDocument(parser)rsrcmgr = PDFResourceManager()laparams = LAParams()device = PDFPageAggregator(rsrcmgr, laparams=laparams)interpreter = PDFPageInterpreter(rsrcmgr, device)for page in PDFPage.create_pages(doc):interpreter.process_page(page)layout = device.get_result()for x in layout:# 獲取文本對象if isinstance(x, LTTextBox):print(x.get_text().strip())# 獲取圖片對象if isinstance(x,LTImage):print('這里獲取到一張圖片')# 獲取 figure 對象if isinstance(x,LTFigure):print('這里獲取到一個 figure 對象') 雖然pdfminer使用門檻較高,但遇到復(fù)雜情況,最后還得用它。目前開源模塊中,它對PDF的支持應(yīng)該是最全的了。
下面這個pdfplumber就是基于pdfminer.six開發(fā)的模塊,降低了使用門檻。
pdfplumber
相比pdfminer.six,pdfplumber提供了更便捷的PDF內(nèi)容抽取接口。
日常工作中常用的操作,比如:
-
提取PDF內(nèi)容,保存到txt文件
提取PDF中的表格到Excel
提取PDF中的圖片
提取PDF中的圖表
提取PDF內(nèi)容,保存到txt文件
import pathlib import pdfplumber path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') out_path = path.joinpath('002pdf_out.txt') with pdfplumber.open(f_path) as pdf, open(out_path ,'a') as txt:for page in pdf.pages:textdata = page.extract_text()txt.write(textdata) 提取PDF中的表格到Excel
import pathlib import pdfplumber from openpyxl import Workbook path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') out_path = path.joinpath('002pdf_excel.xlsx') wb = Workbook() sheet = wb.active with pdfplumber.open(f_path) as pdf:for i in range(19, 22):page = pdf.pages[i]table = page.extract_table()for row in table:sheet.append(row) wb.save(out_path)上面用到了openpyxl的功能創(chuàng)建了一個Excel文件,后面會有單獨文章介紹它。
提取PDF中的圖片
import pathlib import pdfplumber from PIL import Image path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-疫情影響下的中國社區(qū)趨勢研究-艾瑞.pdf') out_path = path.joinpath('002pdf_images.png') with pdfplumber.open(f_path) as pdf, open(out_path, 'wb') as fout:page = pdf.pages[10]# for img in page.images:im = page.to_image()im.save(out_path, format='PNG')imgs = page.imagesfor i, img in enumerate(imgs):size = img['width'], img['height']data = img['stream'].get_data()out_path = path.joinpath(f'002pdf_images_{i}.png')with open(out_path, 'wb') as fimg_out:fimg_out.write(data)上面用到了PIL(Pillow)的功能處理圖片。
提取PDF中的圖表
圖表與圖像不同,指的是類似直方圖、餅圖之類的數(shù)據(jù)生成圖。
import pathlib import pdfplumber from PIL import Image path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf') f_path = path.joinpath('2020-新冠肺炎疫情對中國連鎖餐飲行業(yè)的影響調(diào)研報告-中國連鎖經(jīng)營協(xié)會.pdf') out_path = path.joinpath('002pdf_figures.png') with pdfplumber.open(f_path) as pdf, open(out_path, 'wb') as fout:page = pdf.pages[7]im = page.to_image()im.save(out_path, format='PNG')figures = page.figuresfor i, fig in enumerate(figures):size = fig['width'], fig['height']crop = page.crop((fig['x0'], fig['top'], fig['x1'], fig['bottom']))img_crop = crop.to_image()out_path = path.joinpath(f'002pdf_figures_{i}.png')img_crop.save(out_path, format='png')im.draw_rects(page.extract_words(), stroke='yellow')im.draw_rects(page.images, stroke='blue')im.draw_rects(page.figures) im # show in notebook總結(jié)
以上是生活随笔為你收集整理的pdf各种处理 PDF 的实用代码:PyPDF2、PDFMiner、pdfplumber的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最新出炉-阿里 2020届算法工程师-自
- 下一篇: 论文浅尝 | Reinforcement