python 无损批量压缩图片(支持保留图片信息)的示例


Posted in Python onSeptember 22, 2020

由于云盘空间有限,照片尺寸也是很大,所以写个Python程序压缩一下照片,腾出一些云盘空间

1、批量压缩照片

新建 photo_compress.py 代码如下

# -*- coding: utf-8 -*-

"""脚本功能说明:使用 tinypng api,一键批量压缩指定文件(夹)所有文件"""

import os
import sys
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor # 线程池,进程池
import json
import random
import requests
from you_get import common
from shutil import copyfile


def get_file_dir(file):
 """获取文件目录通用函数"""
 fullpath = os.path.abspath(os.path.realpath(file))
 return os.path.dirname(fullpath)


def check_suffix(file_path):
 """检查指定文件的后缀是否符合要求"""
 file_path_lower = file_path.lower()
 return (file_path_lower.endswith('.png')
   or file_path_lower.endswith('.jpg')
   or file_path_lower.endswith('.jpeg'))


def download_tinypng(input_file, url, output_file):
 file_name = os.path.basename(input_file)
 arr = file_name.split('.')
 new_file_name = arr[len(arr) - 2] + '_compress'
 new_output_file = os.path.join(os.path.dirname(output_file), arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1])
 print(u'开始下载文件 :%s' % new_output_file)
 # print(os.path.splitext(os.path.basename(output_file))[0])
 sys.argv = ['you-get', '-o', os.path.dirname(
  output_file), '-O', new_file_name, url]
 common.main()
 old_size = os.path.getsize(input_file)
 new_size = os.path.getsize(new_output_file)
 print(u'文件保存地址:%s' % new_output_file)
 print(u'压缩后文件大小:%d KB' % (new_size / 1024))
 print(u'压缩比: %d%%' % ((old_size - new_size) * 100 / old_size))


def compress_by_tinypng(input_file):
 if not check_suffix(input_file):
  print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
  return

 file_name = os.path.basename(input_file)
 arr = file_name.split('.')
 new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
 output_path = os.path.join(get_file_dir(input_file), 'compress_output')
 output_file = os.path.join(output_path, new_file_name)
 if not os.path.isdir(output_path):
  os.makedirs(output_path)

 if (os.path.exists(output_file)):
  print("已存在,跳过压缩")
  return

 try:
  old_size = os.path.getsize(input_file)
  print(u'压缩前文件名:%s文件大小:%d KB' % (input_file, old_size / 1024))
  if (old_size < 1024 * 1024):
   print("已跳过压缩,并直接拷贝文件")
   try:
    copyfile(input_file, output_file)
   except IOError as e:
    print("Unable to copy file. %s" % e)
   return
  print("开始压缩")
  shrink_image(input_file)
  print(u'文件压缩成功:%s' % input_file)
  # download_thread_pool.submit(download_tinypng, source, input_file, output_file)
 except Exception as e:
  print(u'报错了:%s' % e)


def check_path(input_path):
 """如果输入的是文件则直接压缩,如果是文件夹则先遍历"""
 if os.path.isfile(input_path):
  compress_by_tinypng(input_path)
 elif os.path.isdir(input_path):
  dirlist = os.walk(input_path)
  for root, dirs, files in dirlist:
   if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
    i = 0
    for filename in files:
     i = i + 1
     process_pool.submit(compress_by_tinypng, os.path.join(
      root, filename))
     # compress_by_tinypng(os.path.join(root, filename))
 else:
  print(u'目标文件(夹)不存在,请确认后重试。')


def list_images(path):
 images = None
 try:
  if path:
   os.chdir(path)
  full_path = os.getcwd()
  files = os.listdir(full_path)
  images = []
  for file in files:
   ext = os.path.splitext(file)[1].lower()
   if ext in ('.jpg', '.jpeg', '.png'):
    images.append(os.path.join(full_path, file))
 except:
  pass
 return images


def shrink_image(file_path):
 print(u'源文件地址:%s' % file_path)
 result = shrink(file_path)
 if result:
  output_path = generate_output_path(file_path)
  url = result['output']['url']
  print(u'下载地址:%s' % url)
  download_tinypng(file_path, url, output_path)
  # download_thread_pool.submit(download_tinypng, file_path, url, output_path)
  # response = requests.get(url)
  # with open(output_path, 'wb') as file:
  #  file.write(response.content)
  # print(u'文件保存地址:%s' % output_path)
  # print('%s %d=>%d(%f)' % (
  #  result['input']['type'],
  #  result['input']['size'],
  #  result['output']['size'],
  #  result['output']['ratio']
  #  ))
 else:
  print('压缩失败')


def shrink(file_path):
 url = 'https://tinypng.com/web/shrink'
 headers = {
  'Cache-Control': 'no-cache',
  'Content-Type': 'application/x-www-form-urlencoded',
  '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',
  'X-Forwarded-For': get_random_ip()
 }
 result = None
 try:
  file = open(file_path, 'rb')
  response = requests.post(url, headers=headers, data=file)
  result = json.loads(response.text)
 except Exception as e:
  print(u'报错了:%s' % e)
  if file:
   file.close()
 if result and result['input'] and result['output']:
  return result
 else:
  return None


def generate_output_path(file_path):
 parent_path = os.path.abspath(os.path.dirname(file_path))
 output_path = os.path.join(parent_path, 'compress_output')
 if not os.path.isdir(output_path):
  os.mkdir(output_path)
 return os.path.join(output_path, os.path.basename(file_path))


def get_random_ip():
 ip = []
 for i in range(4):
  ip.append(str(random.randint(0 if i in (2, 3) else 1, 254)))
 return '.'.join(ip)


if __name__ == '__main__':
 thread_pool = ThreadPoolExecutor(5) # 定义5个线程执行此任务
 download_thread_pool = ThreadPoolExecutor(10) # 定义5个线程执行此任务
 process_pool = ProcessPoolExecutor(8) # 定义5个进程
 len_param = len(sys.argv)
 if len_param != 2 and len_param != 3:
  print('请使用: %s [filepath]' % os.path.basename(sys.argv[0]))
 else:
  check_path(sys.argv[1])
  input("Press <enter> 请耐心等待\n")

执行python .\photo_compress.py F:\\test

python 无损批量压缩图片(支持保留图片信息)的示例

生成compress_output文件夹,里面就是压缩的文件,但此时的照片没有,拍摄时的时间、位置的信息,所以下面要复制文件信息

若要压缩的文件不全,可以再执行一次压缩(会自动过滤已压缩的照片)

2、批量拷贝照片信息

使用pyexiv2进行文件信息拷贝

pip install pyexiv2 -i https://pypi.tuna.tsinghua.edu.cn/simple

新建 copy_fileinfo.py 代码如下

# -*- coding: utf-8 -*-

"""脚本功能说明:使用 pyexiv2 api,一键批量拷贝指定文件(夹)所有文件信息"""

import os
import sys
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor # 线程池,进程池
from pyexiv2 import Image


def get_file_dir(file):
 """获取文件目录通用函数"""
 fullpath = os.path.abspath(os.path.realpath(file))
 return os.path.dirname(fullpath)


def check_suffix(file_path):
 """检查指定文件的后缀是否符合要求"""
 file_path_lower = file_path.lower()
 return (file_path_lower.endswith('.png')
   or file_path_lower.endswith('.jpg')
   or file_path_lower.endswith('.jpeg'))


def copyinfo_by_pyexiv2(input_file):
 file_name = os.path.basename(input_file)
 arr = file_name.split('.')
 new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
 output_path = os.path.join(get_file_dir(input_file), 'compress_output')
 output_file = os.path.join(output_path, new_file_name)
 if not (check_suffix(input_file) or check_suffix(output_file)):
  print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
  return
 if not (os.path.exists(output_file)):
  print(u'文件不存在:' + output_file)
  return
 old_size = os.path.getsize(input_file)
 if (old_size < 1024 * 1024):
  print(u"已跳过拷贝文件信息:", input_file)
  return

 # if not os.path.isdir(output_path):
 #  os.makedirs(output_path)
 try:
  i = Image(input_file) # 源图片路径
 except Exception:
  i = Image(input_file, "GB18030")

 try:
  _exif_info = i.read_exif()
 except Exception:
  _exif_info = i.read_exif("GB18030")

 # print(_exif_info)
 # _iptc_info = i.read_iptc()
 # print(_iptc_info)
 # _xmp_info = i.read_xmp()
 # print(_xmp_info)
 i.close()

 try:
  i2 = Image(output_file) # 拷贝信息图片路径
 except Exception:
  i2 = Image(output_file, "GB18030")

 try:
  _exif_info2 = i2.read_exif()
 except Exception:
  _exif_info2 = i2.read_exif("GB18030")

 # 方向不拷贝,防止图片旋转
 for item in _exif_info:
  if("Exif.Image.Orientation" != item):
   if (_exif_info2.get(item) != _exif_info.get(item)):
    try:
     i2.modify_exif({item: _exif_info[item]})
    except Exception as e:
     print(e)
     try:
      i2.modify_exif({item: _exif_info[item]}, "GB18030")
     except Exception as e:
      print(e)

 i2.close()

 print(u"拷贝信息完成:" + input_file)


def check_path(input_path):
 """如果输入的是文件则直接压缩,如果是文件夹则先遍历"""
 if os.path.isfile(input_path):
  copyinfo_by_pyexiv2(input_path)
 elif os.path.isdir(input_path):
  dirlist = os.walk(input_path)
  for root, dirs, files in dirlist:
   if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
    i = 0
    for filename in files:
     i = i + 1
     process_pool.submit(copyinfo_by_pyexiv2, os.path.join(
      root, filename))
 else:
  print(u'目标文件(夹)不存在,请确认后重试。')


if __name__ == '__main__':
 # thread_pool = ThreadPoolExecutor(10) # 定义5个线程执行此任务
 process_pool = ProcessPoolExecutor(8) # 定义5个进程
 len_param = len(sys.argv)
 if len_param != 2:
  print('请使用: %s [filepath]' % os.path.basename(sys.argv[0]))
 else:
  check_path(sys.argv[1])
  input("Press <enter> 请耐心等待\n")

执行python .\copy_fileinfo.py F:\\test

大功告成!图片压缩完毕,信息还没有丢失

以上就是python 无损批量压缩图片(支持保留图片信息)的示例的详细内容,更多关于python 无损批量压缩图片的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python中使用装饰器和元编程实现结构体类实例
Jan 28 Python
Python中文件操作简明介绍
Apr 13 Python
python修改操作系统时间的方法
May 18 Python
解决pycharm py文件运行后停止按钮变成了灰色的问题
Nov 29 Python
Python中extend和append的区别讲解
Jan 24 Python
Python 调用 Windows API COM 新法
Aug 22 Python
tensorflow实现训练变量checkpoint的保存与读取
Feb 10 Python
Python环境下安装PyGame和PyOpenGL的方法
Mar 25 Python
使用python计算三角形的斜边例子
Apr 15 Python
Python pip安装模块提示错误解决方案
May 22 Python
Selenium自动化测试工具使用方法汇总
Jun 12 Python
Matlab中plot基本用法的具体使用
Jul 17 Python
Pytho爬虫中Requests设置请求头Headers的方法
Sep 22 #Python
python 线程的五个状态
Sep 22 #Python
python 如何实现遗传算法
Sep 22 #Python
利用python汇总统计多张Excel
Sep 22 #Python
爬虫代理的cookie如何生成运行
Sep 22 #Python
python 如何将office文件转换为PDF
Sep 22 #Python
Python制作一个仿QQ办公版的图形登录界面
Sep 22 #Python
You might like
PHP加密解密类实例分析
2015/04/20 PHP
PHP simplexml_load_file()函数讲解
2019/02/03 PHP
用Javascript做flash做的事..才完成的一个类.Auntion Action var 0.1
2007/02/23 Javascript
checkbox使用示例
2013/08/23 Javascript
jquery parent和parents的区别分析
2013/10/02 Javascript
jquery中push()的用法(数组添加元素)
2014/11/25 Javascript
node.js中的events.emitter.listeners方法使用说明
2014/12/10 Javascript
浅谈Javascript Base64 加密解密
2014/12/28 Javascript
JS获取下拉框显示值和判断单选按钮的方法
2015/07/09 Javascript
分享使用AngularJS创建应用的5个框架
2015/12/05 Javascript
原生javascript实现自动更新的时间日期
2016/02/12 Javascript
JavaScript中instanceof运算符的使用示例
2016/06/08 Javascript
详解jQuery uploadify文件上传插件的使用方法
2016/12/16 Javascript
利用vue实现模态框组件
2016/12/19 Javascript
Node.js 异步异常的处理与domain模块解析
2017/05/10 Javascript
vue实现选项卡及选项卡切换效果
2018/04/24 Javascript
JavaScript求一组数的最小公倍数和最大公约数常用算法详解【面向对象,回归迭代和循环】
2018/05/07 Javascript
JavaScript实现图片轮播特效
2019/10/23 Javascript
VUE 实现element upload上传图片到阿里云
2020/08/12 Javascript
python解决字典中的值是列表问题的方法
2013/03/04 Python
Python3实现带附件的定时发送邮件功能
2020/12/22 Python
python中itertools模块zip_longest函数详解
2018/06/12 Python
Python for循环生成列表的实例
2018/06/15 Python
Python爬虫的两套解析方法和四种爬虫实现过程
2018/07/20 Python
Python使用LDAP做用户认证的方法
2019/06/20 Python
用python对excel查重
2020/12/07 Python
Coltorti Boutique官网:来自意大利的设计师品牌买手店
2018/11/09 全球购物
信息管理专业学生自荐信格式
2013/09/22 职场文书
八一建军节部队活动方案
2014/02/04 职场文书
党员创先争优公开承诺书
2014/03/28 职场文书
公司联欢会策划方案
2014/05/19 职场文书
银行员工犯错检讨书
2014/09/16 职场文书
2015年全国“爱牙日”宣传活动总结
2015/03/23 职场文书
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
2021/06/30 SQL Server
JavaScript获取URL参数的方法分享
2022/04/07 Javascript
Win11软件图标固定到任务栏
2022/04/19 数码科技