python 无损压缩照片,支持批量压缩,支持保留照片信息
由于云盘空间有限,照片尺寸也是很大,所以写个Python程序压缩一下照片,腾出一些云盘空间
1、批量压缩照片
新建 photo_compress.py 代码如下
1 # -*- coding: utf-8 -*-
2
3 """脚本功能说明:使用 tinypng api,一键批量压缩指定文件(夹)所有文件"""
4
5 import os
6 import sys
7 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor # 线程池,进程池
8 import json
9 import random
10 import requests
11 from you_get import common
12 from shutil import copyfile
13
14
15 def get_file_dir(file):
16 """获取文件目录通用函数"""
17 fullpath = os.path.abspath(os.path.realpath(file))
18 return os.path.dirname(fullpath)
19
20
21 def check_suffix(file_path):
22 """检查指定文件的后缀是否符合要求"""
23 file_path_lower = file_path.lower()
24 return (file_path_lower.endswith('.png')
25 or file_path_lower.endswith('.jpg')
26 or file_path_lower.endswith('.jpeg'))
27
28
29 def download_tinypng(input_file, url, output_file):
30 file_name = os.path.basename(input_file)
31 arr = file_name.split('.')
32 new_file_name = arr[len(arr) - 2] + '_compress'
33 new_output_file = os.path.join(os.path.dirname(output_file), arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1])
34 print(u'开始下载文件 :%s' % new_output_file)
35 # print(os.path.splitext(os.path.basename(output_file))[0])
36 sys.argv = ['you-get', '-o', os.path.dirname(
37 output_file), '-O', new_file_name, url]
38 common.main()
39 old_size = os.path.getsize(input_file)
40 new_size = os.path.getsize(new_output_file)
41 print(u'文件保存地址:%s' % new_output_file)
42 print(u'压缩后文件大小:%d KB' % (new_size / 1024))
43 print(u'压缩比: %d%%' % ((old_size - new_size) * 100 / old_size))
44
45
46 def compress_by_tinypng(input_file):
47 if not check_suffix(input_file):
48 print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
49 return
50
51 file_name = os.path.basename(input_file)
52 arr = file_name.split('.')
53 new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
54 output_path = os.path.join(get_file_dir(input_file), 'compress_output')
55 output_file = os.path.join(output_path, new_file_name)
56 if not os.path.isdir(output_path):
57 os.makedirs(output_path)
58
59 if (os.path.exists(output_file)):
60 print("已存在,跳过压缩")
61 return
62
63 try:
64 old_size = os.path.getsize(input_file)
65 print(u'压缩前文件名:%s文件大小:%d KB' % (input_file, old_size / 1024))
66 if (old_size < 1024 * 1024):
67 print("已跳过压缩,并直接拷贝文件")
68 try:
69 copyfile(input_file, output_file)
70 except IOError as e:
71 print("Unable to copy file. %s" % e)
72 return
73 print("开始压缩")
74 shrink_image(input_file)
75 print(u'文件压缩成功:%s' % input_file)
76 # download_thread_pool.submit(download_tinypng, source, input_file, output_file)
77 except Exception as e:
78 print(u'报错了:%s' % e)
79
80
81 def check_path(input_path):
82 """如果输入的是文件则直接压缩,如果是文件夹则先遍历"""
83 if os.path.isfile(input_path):
84 compress_by_tinypng(input_path)
85 elif os.path.isdir(input_path):
86 dirlist = os.walk(input_path)
87 for root, dirs, files in dirlist:
88 if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
89 i = 0
90 for filename in files:
91 i = i + 1
92 process_pool.submit(compress_by_tinypng, os.path.join(
93 root, filename))
94 # compress_by_tinypng(os.path.join(root, filename))
95 else:
96 print(u'目标文件(夹)不存在,请确认后重试。')
97
98
99 def list_images(path):
100 images = None
101 try:
102 if path:
103 os.chdir(path)
104 full_path = os.getcwd()
105 files = os.listdir(full_path)
106 images = []
107 for file in files:
108 ext = os.path.splitext(file)[1].lower()
109 if ext in ('.jpg', '.jpeg', '.png'):
110 images.append(os.path.join(full_path, file))
111 except:
112 pass
113 return images
114
115
116 def shrink_image(file_path):
117 print(u'源文件地址:%s' % file_path)
118 result = shrink(file_path)
119 if result:
120 output_path = generate_output_path(file_path)
121 url = result['output']['url']
122 print(u'下载地址:%s' % url)
123 download_tinypng(file_path, url, output_path)
124 # download_thread_pool.submit(download_tinypng, file_path, url, output_path)
125 # response = requests.get(url)
126 # with open(output_path, 'wb') as file:
127 # file.write(response.content)
128 # print(u'文件保存地址:%s' % output_path)
129 # print('%s %d=>%d(%f)' % (
130 # result['input']['type'],
131 # result['input']['size'],
132 # result['output']['size'],
133 # result['output']['ratio']
134 # ))
135 else:
136 print('压缩失败')
137
138
139 def shrink(file_path):
140 url = 'https://tinypng.com/web/shrink'
141 headers = {
142 'Cache-Control': 'no-cache',
143 'Content-Type': 'application/x-www-form-urlencoded',
144 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44',
145 'X-Forwarded-For': get_random_ip()
146 }
147 result = None
148 try:
149 file = open(file_path, 'rb')
150 response = requests.post(url, headers=headers, data=file)
151 result = json.loads(response.text)
152 except Exception as e:
153 print(u'报错了:%s' % e)
154 if file:
155 file.close()
156 if result and result['input'] and result['output']:
157 return result
158 else:
159 return None
160
161
162 def generate_output_path(file_path):
163 parent_path = os.path.abspath(os.path.dirname(file_path))
164 output_path = os.path.join(parent_path, 'compress_output')
165 if not os.path.isdir(output_path):
166 os.mkdir(output_path)
167 return os.path.join(output_path, os.path.basename(file_path))
168
169
170 def get_random_ip():
171 ip = []
172 for i in range(4):
173 ip.append(str(random.randint(0 if i in (2, 3) else 1, 254)))
174 return '.'.join(ip)
175
176
177 if __name__ == '__main__':
178 thread_pool = ThreadPoolExecutor(5) # 定义5个线程执行此任务
179 download_thread_pool = ThreadPoolExecutor(10) # 定义5个线程执行此任务
180 process_pool = ProcessPoolExecutor(8) # 定义5个进程
181 len_param = len(sys.argv)
182 if len_param != 2 and len_param != 3:
183 print('请使用: %s [filepath]' % os.path.basename(sys.argv[0]))
184 else:
185 check_path(sys.argv[1])
186 input("Press <enter> 请耐心等待\n")
执行python .\photo_compress.py F:\\test

生成compress_output文件夹,里面就是压缩的文件,但此时的照片没有,拍摄时的时间、位置的信息,所以下面要复制文件信息
若要压缩的文件不全,可以再执行一次压缩(会自动过滤已压缩的照片)
2、批量拷贝照片信息
使用pyexiv2进行文件信息拷贝
pip install pyexiv2 -i https://pypi.tuna.tsinghua.edu.cn/simple
新建 copy_fileinfo.py 代码如下
1 # -*- coding: utf-8 -*-
2
3 """脚本功能说明:使用 pyexiv2 api,一键批量拷贝指定文件(夹)所有文件信息"""
4
5 import os
6 import sys
7 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor # 线程池,进程池
8 from pyexiv2 import Image
9
10
11 def get_file_dir(file):
12 """获取文件目录通用函数"""
13 fullpath = os.path.abspath(os.path.realpath(file))
14 return os.path.dirname(fullpath)
15
16
17 def check_suffix(file_path):
18 """检查指定文件的后缀是否符合要求"""
19 file_path_lower = file_path.lower()
20 return (file_path_lower.endswith('.png')
21 or file_path_lower.endswith('.jpg')
22 or file_path_lower.endswith('.jpeg'))
23
24
25 def copyinfo_by_pyexiv2(input_file):
26 file_name = os.path.basename(input_file)
27 arr = file_name.split('.')
28 new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
29 output_path = os.path.join(get_file_dir(input_file), 'compress_output')
30 output_file = os.path.join(output_path, new_file_name)
31 if not (check_suffix(input_file) or check_suffix(output_file)):
32 print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
33 return
34 if not (os.path.exists(output_file)):
35 print(u'文件不存在:' + output_file)
36 return
37 old_size = os.path.getsize(input_file)
38 if (old_size < 1024 * 1024):
39 print(u"已跳过拷贝文件信息:", input_file)
40 return
41
42 # if not os.path.isdir(output_path):
43 # os.makedirs(output_path)
44 try:
45 i = Image(input_file) # 源图片路径
46 except Exception:
47 i = Image(input_file, "GB18030")
48
49 try:
50 _exif_info = i.read_exif()
51 except Exception:
52 _exif_info = i.read_exif("GB18030")
53
54 # print(_exif_info)
55 # _iptc_info = i.read_iptc()
56 # print(_iptc_info)
57 # _xmp_info = i.read_xmp()
58 # print(_xmp_info)
59 i.close()
60
61 try:
62 i2 = Image(output_file) # 拷贝信息图片路径
63 except Exception:
64 i2 = Image(output_file, "GB18030")
65
66 try:
67 _exif_info2 = i2.read_exif()
68 except Exception:
69 _exif_info2 = i2.read_exif("GB18030")
70
71 # 方向不拷贝,防止图片旋转
72 for item in _exif_info:
73 if("Exif.Image.Orientation" != item):
74 if (_exif_info2.get(item) != _exif_info.get(item)):
75 try:
76 i2.modify_exif({item: _exif_info[item]})
77 except Exception as e:
78 print(e)
79 try:
80 i2.modify_exif({item: _exif_info[item]}, "GB18030")
81 except Exception as e:
82 print(e)
83
84 i2.close()
85
86 print(u"拷贝信息完成:" + input_file)
87
88
89 def check_path(input_path):
90 """如果输入的是文件则直接压缩,如果是文件夹则先遍历"""
91 if os.path.isfile(input_path):
92 copyinfo_by_pyexiv2(input_path)
93 elif os.path.isdir(input_path):
94 dirlist = os.walk(input_path)
95 for root, dirs, files in dirlist:
96 if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
97 i = 0
98 for filename in files:
99 i = i + 1
100 process_pool.submit(copyinfo_by_pyexiv2, os.path.join(
101 root, filename))
102 else:
103 print(u'目标文件(夹)不存在,请确认后重试。')
104
105
106 if __name__ == '__main__':
107 # thread_pool = ThreadPoolExecutor(10) # 定义5个线程执行此任务
108 process_pool = ProcessPoolExecutor(8) # 定义5个进程
109 len_param = len(sys.argv)
110 if len_param != 2:
111 print('请使用: %s [filepath]' % os.path.basename(sys.argv[0]))
112 else:
113 check_path(sys.argv[1])
114 input("Press <enter> 请耐心等待\n")
执行python .\copy_fileinfo.py F:\\test
大功告成!图片压缩完毕,信息还没有丢失
python 无损压缩照片,支持批量压缩,支持保留照片信息的更多相关文章
- python监控文件实时批量压缩脚本
# coding:utf-8 from shutil import make_archive import os import time # 指定需要监测的文件夹 image_path = './im ...
- 10 行 Python 代码,批量压缩图片 500 张,简直太强大了
本文原创并首发于公众号[Python猫],未经授权,请勿转载. 原文地址:https://mp.weixin.qq.com/s/5hpFDgjCpfb0O1Jg-ycACw 熟悉 "Pyth ...
- Python实现邮件的批量发送
Python实现邮件的批量发送 1 发送文本信息 '''加密发送文本邮件''' def sendEmail(from_addr,password,to_addr,smtp_server): try: ...
- 使用Python轻松批量压缩图片
在互联网,图片的大小对一个网站的响应速度有着明显的影响,因此在提供用户预览的时候,图片往往是使用压缩后的.如果一个网站图片较多,一张张压缩显然很浪费时间.那么接下来,我就跟大家分享一个批量压缩图片的方 ...
- python模块:网络协议和支持
python模块:网络协议和支持 webbrowser 调用浏览器显示html文件 webbrowser.open('map.html') [webbrowser - Convenient Web-b ...
- [原创]开源跨平台大型网络端口扫描器K8PortScan(支持批量A段/B段/C段/IP列表)
0x000 K8PortScan Python版Cscan端口扫描器 Code: https://github.com/k8gege/K8PortScan K8portScan 1.0 Date: 2 ...
- oracle+ibatis 批量插入-支持序列自增
首先请先看我前面一篇帖子了解oracle批量插入的sql:[oracle 批量插入-支持序列自增] 我用的ibatis2.0,sqlMap文件引入的标签如下: <!DOCTYPE sqlMap ...
- oracle 批量插入-支持序列自增
1.创建表.序列 -- Create table create table test_batch ( id number not null, name ), account ) ) -- Create ...
- 解剖SQLSERVER 第十二篇 OrcaMDF 行压缩支持(译)
解剖SQLSERVER 第十二篇 OrcaMDF 行压缩支持(译) http://improve.dk/orcamdf-row-compression-support/ 在这两个月的断断续续的开发 ...
随机推荐
- Folx使用教程:怎么通过设置标签分类下载内容
很多Mac OS下载软件从网上下载各种各样的文件,一般默认都会存放在"下载"文件夹中.如果不是经常整理"下载"文件夹,久而久之,该文件夹会变得庞大而杂乱. 如果 ...
- guitar pro系列教程(四): 详解Guitar Pro主音量自动化设置
让我们继续进行guitar pro 7系列教程 在上一章节中我们讲到插入速度自动化设置,本章节我们将采用图文结合的方式详细的讲解guitar pro 7主音量的相关自动化设置分别是:插入主音量自动化, ...
- python连接mysql循环插入千万条数据脚本
之前都是在mysql的存储过程中插入数据,毕竟mysql语法函数有限,很多都有限制.突然想到学了python正好可以练练手.首先需要安装pymysql模块包(模块包安装请自行百度) pip insta ...
- vue+element ctrl+s保存写法
<el-input type="textarea" ref="inppp" v-model="values" placeholder= ...
- 关闭Win10窗口拖动到桌面边缘自动缩放功能
- 在之前的EventHandler中的参数类型必须继承EventArgs,现在已经去掉这个约束了。
分别是vs2008和vs2012的对比,可以看到2012已经去掉了约束条件.
- 部署完的Django项目升级为HTTPS
1.阿里云上申请免费ssl证书--->提交各种资料--->等待审核--->下载证书. 2.远程连接阿里云服务器,将下载下来的证书内容复制到Nginx安装目录下的cert目录(需要新建 ...
- PyQt(Python+Qt)学习随笔:QListWidget的currentRow属性
QListWidget的currentRow属性保存当前项的位置,为整型,从0开始计数,在某些选择模式下,当前项可能也是选中项. currentRow属性可以通过方法currentRow().setC ...
- 【Docker】 使用Docker 在阿里云 Centos7 部署 MySQL 和 Redis (二)
系列目录: [Docker] CentOS7 安装 Docker 及其使用方法 ( 一 ) [Docker] 使用Docker 在阿里云 Centos7 部署 MySQL 和 Redis (二) [D ...
- 性能测试学习之路 (一)认识jmeter(性能测试流程 && 性能测试通过标准 &&jmeter体系结构)
性能测试是通过自动化的测试工具模拟多种正常.峰值以及异常负载条件来对系统的各项性能指标进行测试. 1 性能测试技能树 性能测试是一项综合性的工作,致力于暴露性能问题,评估系统性能趋势.性能测试工作实质 ...