日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

用python做PDF本地化压缩,新增多进程

發(fā)布時(shí)間:2023/12/20 python 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用python做PDF本地化压缩,新增多进程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

用python做PDF壓縮

雖然現(xiàn)在有很多成熟的工具了,但是就是想自己搗鼓一下

在網(wǎng)上找了一圈,發(fā)現(xiàn)實(shí)現(xiàn)方法有兩種,一種是需要聯(lián)網(wǎng)上傳(TinyPNG的API)壓縮的,一種是本地用python算法

這里采用的是本地,基本的思路是

1、提取PDF內(nèi)容,保存成圖片

2、壓縮圖片

3、圖片合成PDF
4、新增加入多進(jìn)程和隊(duì)列的方式,加快壓縮
聯(lián)網(wǎng)上傳的我覺得直接用i love pdf這個(gè)網(wǎng)頁,挺好用的,就不知道安不安全。。。

Compress PDF online. Same PDF quality less file size (ilovepdf.com)

這里參考了兩篇博客的代碼

(2條消息) Python實(shí)現(xiàn)PDF文件壓縮_xinxinbupp的博客-CSDN博客

(2條消息) Python-從PDF中提取圖片、壓縮PDF_xinRCNN的博客-CSDN博客

但是感覺壓縮出來的圖片不是很理想,就想找一個(gè)圖片壓縮算法替換上去

在網(wǎng)上找到一個(gè)python的圖片壓縮算法,說是**“可能是最接近微信朋友圈的圖片壓縮算法”**

GitHub - Freefighter/Luban-Py: Python version of Luban(魯班)—Image compression with efficiency very close to WeChat Moments/可能是最接近微信朋友圈的圖片壓縮算法

依賴安裝

先安裝庫(kù) fitz,再安裝庫(kù)pymupdf,地址:https://github.com/pymupdf/PyMuPDF/

pip install fitzpip install PyMuPDF pip install easygui # 用來彈出文件選擇框的,thinker的話會(huì)彈出兩個(gè)窗口怪怪的

縫合修改

CV大法用上

# -*- coding:utf-8 -*- # author: peng # file: mypdf.py # time: 2021/9/8 17:47 # desc:壓縮PDF,對(duì)純圖片的PDF效果效果較好,有文字內(nèi)容的可能會(huì)比較模糊,推薦高質(zhì)量的壓縮import fitz from PIL import Image import os from shutil import copyfile, rmtree from math import ceilfrom time import strftime, localtime, time import easygui as gfrom functools import wraps # 時(shí)間計(jì)數(shù)裝飾器 def runtime(func):@wraps(func)def wrapper(*args, **kwargs):print(strftime("%Y-%m-%d %H:%M:%S", localtime()))start = time()func(*args, **kwargs)end = time()print(func.__name__, args[-1], args[-2], " spend time ", end - start, " sec")return wrapperclass Luban(object):def __init__(self, quality, ignoreBy=102400):self.ignoreBy = ignoreByself.quality = qualitydef setPath(self, path):self.path = pathdef setTargetDir(self, foldername="target"):self.dir, self.filename = os.path.split(self.path)self.targetDir = os.path.join(self.dir, foldername)if not os.path.exists(self.targetDir):os.makedirs(self.targetDir)self.targetPath = os.path.join(self.targetDir, "c_" + self.filename)def load(self):self.img = Image.open(self.path)if self.img.mode == "RGB":self.type = "JPEG"elif self.img.mode == "RGBA":self.type = "PNG"else: # 其他的圖片就轉(zhuǎn)成JPEGself.img = self.img.convert("RGB")self.type = "JPEG"def computeScale(self):# 計(jì)算縮小的倍數(shù)srcWidth, srcHeight = self.img.sizesrcWidth = srcWidth + 1 if srcWidth % 2 == 1 else srcWidthsrcHeight = srcHeight + 1 if srcHeight % 2 == 1 else srcHeightlongSide = max(srcWidth, srcHeight)shortSide = min(srcWidth, srcHeight)scale = shortSide / longSideif (scale <= 1 and scale > 0.5625):if (longSide < 1664):return 1elif (longSide < 4990):return 2elif (longSide > 4990 and longSide < 10240):return 4else:return max(1, longSide // 1280)elif (scale <= 0.5625 and scale > 0.5):return max(1, longSide // 1280)else:return ceil(longSide / (1280.0 / scale))def compress(self):self.setTargetDir()# 先調(diào)整大小,再調(diào)整品質(zhì)if os.path.getsize(self.path) <= self.ignoreBy:copyfile(self.path, self.targetPath)else:self.load()scale = self.computeScale()srcWidth, srcHeight = self.img.sizecache = self.img.resize((srcWidth // scale, srcHeight // scale),Image.ANTIALIAS)cache.save(self.targetPath, self.type, quality=self.quality)# 提取成圖片 def covert2pic(doc, totaling, zooms=None):''':param totaling: pdf的頁數(shù):param zooms: 值越大,分辨率越高,文件越清晰,列表內(nèi)兩個(gè)浮點(diǎn)數(shù),每個(gè)尺寸的縮放系數(shù),默認(rèn)為分辨率的2倍:return:'''if zooms is None:zooms = [2.0, 2.0]if os.path.exists('.pdf'): # 臨時(shí)文件,需為空rmtree('.pdf')os.mkdir('.pdf')print(f"pdf頁數(shù)為 {totaling} \n創(chuàng)建臨時(shí)文件夾.....")for pg in range(totaling):page = doc[pg]print(f"\r{page}", end="")trans = fitz.Matrix(*zooms).preRotate(0) # 0為旋轉(zhuǎn)角度pm = page.getPixmap(matrix=trans, alpha=False)lurl = '.pdf/%s.jpg' % str(pg + 1)pm.writePNG(lurl) #保存doc.close()# 圖片合成pdf def pic2pdf(obj, ratio, totaling):doc = fitz.open()compressor = Luban(quality=ratio)for pg in range(totaling):path = '.pdf/%s.jpg' % str(pg + 1)compressor.setPath(path)compressor.compress()print(f"\r 插入圖片 {pg + 1}/{totaling} 中......", end="")img = '.pdf/target/c_%s.jpg' % str(pg + 1)imgdoc = fitz.open(img) # 打開圖片pdfbytes = imgdoc.convertToPDF() # 使用圖片創(chuàng)建單頁的 PDFos.remove(img)imgpdf = fitz.open("pdf", pdfbytes)doc.insertPDF(imgpdf) # 將當(dāng)前頁插入文檔if os.path.exists(obj): # 若pdf文件存在先刪除os.remove(obj)doc.save(obj) # 保存pdf文件doc.close()@runtime def pdfz(doc, obj, ratio, totaling):covert2pic(doc, totaling)pic2pdf(obj, ratio, totaling)def pic_quality():print("輸入壓縮等級(jí)1~3:")comp_level = input("壓縮等級(jí)(1=高畫質(zhì)50%,2=中畫質(zhì)70%,3=低畫質(zhì)80%):(輸入數(shù)字并按回車鍵)")# 用字典模擬Switch分支,注意輸入的值是str類型ratio = {'1': 40, '2': 20, '3': 10}# 字典中沒有則默認(rèn) 低畫質(zhì)壓縮return ratio.get(comp_level, 10)if __name__ == "__main__":print("請(qǐng)選擇需要壓縮的PDF文件")while True:'''打開選擇文件夾對(duì)話框'''filepath = g.fileopenbox(title=u"選擇PDF", filetypes=['*.pdf'])if filepath == None:input("還未選擇文件,輸入任意鍵繼續(xù).......")continueelse:filedir, filename = os.path.split(filepath)print(u'已選中文件【%s】' % (filename))if filename.endswith(".pdf") == False:input("選擇的文件類型不對(duì),輸入任意鍵繼續(xù).......")continueratio = pic_quality()obj = "new_" + filenamedoc = fitz.open(filepath)totaling = doc.pageCountpdfz(doc, obj, ratio, totaling)rmtree('.pdf')oldsize = os.stat(filepath).st_sizenewsize = os.stat(obj).st_sizeprint('壓縮結(jié)果 %.2f M >>>> %.2f M'%(oldsize/(1024 * 1024),newsize/(1024 * 1024)))input(f"壓縮已完成,文件保存在改程序目錄下{filedir},如需繼續(xù)壓縮請(qǐng)按任意鍵")

效果


壓縮出來的結(jié)果:

當(dāng)然,不是所有的pdf壓縮都會(huì)變小。。。本身pdf文件小的,處理出來后可能會(huì)變大,原因應(yīng)該是圖片提取保存的時(shí)候圖片文件變大,所有壓縮進(jìn)去的時(shí)候也會(huì)變大。

新增多進(jìn)程

在使用過多線程時(shí),發(fā)現(xiàn)速度沒什么提升,因?yàn)檫@個(gè)程序CPU和IO都有占用,大家可以測(cè)試在多線程和多進(jìn)程下哪個(gè)速度快就采用哪個(gè)
Python中單線程、多線程和多進(jìn)程的效率對(duì)比實(shí)驗(yàn)
Python并發(fā)編程之多進(jìn)程

別的博客中說到:“需要注意的是隊(duì)列中Queue.Queue是線程安全的,但并不是進(jìn)程安全,所以多進(jìn)程一般使用線程、進(jìn)程安全的multiprocessing.Queue(),而使用這個(gè)Queue如果數(shù)據(jù)量太大會(huì)導(dǎo)致進(jìn)程莫名卡住(絕壁大坑來的),需要不斷地消費(fèi)?!?/strong>
這里對(duì)代碼的修改部分有幾個(gè)小地方,提取圖片的參數(shù)變?yōu)閜df路徑(因?yàn)閐oc參數(shù)在進(jìn)程調(diào)用時(shí)會(huì)出錯(cuò)),隊(duì)列
轉(zhuǎn)pdf內(nèi)部加入判斷隊(duì)列為空和取操作,這樣就簡(jiǎn)單實(shí)現(xiàn)了生產(chǎn)者-消費(fèi)者模式

from multiprocessing import Process, Queue# 提取成圖片 def covert2pic(filepath, qpaper, zooms=None):''':param filepath: pdf文件的位置:param qpaper: 數(shù)據(jù)頁的隊(duì)列:param zooms: 值越大,分辨率越高,文件越清晰,列表內(nèi)兩個(gè)浮點(diǎn)數(shù),每個(gè)尺寸的縮放系數(shù),默認(rèn)為分辨率的2倍:return:'''doc = fitz.open(filepath)totaling = doc.pageCountif zooms is None:zooms = [2.0, 2.0]if path.exists('.pdf'): # 臨時(shí)文件,需為空rmtree('.pdf')mkdir('.pdf')print(f"pdf頁數(shù)為 {totaling} \n創(chuàng)建臨時(shí)文件夾.....")for pg in range(totaling):page = doc[pg]print(f"\r{page}", end="")trans = fitz.Matrix(*zooms).preRotate(0) # 0為旋轉(zhuǎn)角度pm = page.getPixmap(matrix=trans, alpha=False)lurl = '.pdf/%s.jpg' % str(pg + 1)pm.writePNG(lurl) # 保存qpaper.put(pg)doc.close()# 圖片合成pdf def pic2pdf(obj, ratio, qpaper, totaling):doc2 = fitz.open()compressor = Luban(quality=ratio)for pg in range(totaling):picpath = '.pdf/%s.jpg' % str(pg + 1)compressor.setPath(picpath)while qpaper.empty():# 如果隊(duì)列為空,則循環(huán)等待passqpaper.get()compressor.compress()print(f"\r 插入圖片 {pg + 1}/{totaling} 中......", end="")img = '.pdf/target/c_%s.jpg' % str(pg + 1)imgdoc = fitz.open(img) # 打開圖片pdfbytes = imgdoc.convertToPDF() # 使用圖片創(chuàng)建單頁的 PDFremove(img)imgpdf = fitz.open("pdf", pdfbytes)doc2.insertPDF(imgpdf) # 將當(dāng)前頁插入文檔if path.exists(obj): # 若pdf文件存在先刪除remove(obj)doc2.save(obj) # 保存pdf文件doc2.close()@runtime def pdfz(filepath, obj, ratio, totaling): # 參數(shù)傳遞變?yōu)閒ilepathqpaper = Queue() # 創(chuàng)建隊(duì)列threads = []#read_thread = threading.Thread(target=covert2pic, args=(doc, totaling, qpaper))read_thread = Process(target=covert2pic, args=(filepath, qpaper))'''多進(jìn)程這里傳參數(shù)不一定成功,參數(shù)需要可以序列化才行,這里如果傳doc的變量,會(huì)報(bào)錯(cuò)WeakValueDictionary.__init__.<locals>.remove'''threads.append(read_thread)#write_thread = threading.Thread(target=pic2pdf, args=(obj, ratio, totaling, qpaper))write_thread = Process(target=pic2pdf, args=(obj, ratio, qpaper, totaling))threads.append(write_thread)for th in threads:th.start() # 開始執(zhí)行線程for th in threads:th.join()print("結(jié)束")

最終多進(jìn)程會(huì)比單進(jìn)程節(jié)約大約30%的時(shí)間(節(jié)約了處理圖片和生成pdf的時(shí)間,就是函數(shù)pic2pdf

缺點(diǎn)

  • 使用的不是GUI界面,沒那么美觀,感覺也沒必要吧哈哈哈

  • 提取文件的時(shí)候比較慢,想著多線程但是不會(huì),可能要對(duì)文件分塊,還是算了

  • 用pyinstaller(本人在conda創(chuàng)建的虛擬環(huán)境下python2.6打包出來小一點(diǎn))打包出來,文件大小差不多30M,而且打包之后運(yùn)行就沒那么流暢了,而且有個(gè)坑點(diǎn)

    執(zhí)行過程在cmd黑窗口中打印信息時(shí),有時(shí),一不小心鼠標(biāo)點(diǎn)到了黑窗口里,程序就會(huì)暫停,要回車才能繼續(xù),網(wǎng)上的說法是

    “或許是cmd啟用了快速編輯模式導(dǎo)致的問題。在快速編輯模式,鼠標(biāo)點(diǎn)擊cmd窗口時(shí),可以直接選擇窗口里的文本,如果此時(shí)cmd中運(yùn)行的進(jìn)程需要在cmd窗口中輸出信息,這個(gè)進(jìn)程就會(huì)被暫停,直到按下回車?!?/strong>

    解決方法:Python 解決cmd窗口鼠標(biāo)點(diǎn)擊后掛起不執(zhí)行問題(禁止快速編輯模式)_淺醉櫻花雨的專欄-CSDN博客

    加入代碼:但是沒用。。。輸入的時(shí)候會(huì)輸入不了,暫時(shí)不加了

    可以看下邊的方法,只對(duì)cmd設(shè)置而已

    Windows下CMD(命令提示符)腳本運(yùn)行過程中被阻塞_VisionLRJ的博客-CSDN博客_cmd阻塞

總結(jié)

以上是生活随笔為你收集整理的用python做PDF本地化压缩,新增多进程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。