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获取Linux下文件版本信息、公司名和产品名的方法
Oct 05 Python
自己编程中遇到的Python错误和解决方法汇总整理
Jun 03 Python
Python 字典与字符串的互转实例
Jan 13 Python
在CMD命令行中运行python脚本的方法
May 12 Python
django输出html内容的实例
May 27 Python
Python计算一个给定时间点前一个月和后一个月第一天的方法
May 29 Python
Django集成搜索引擎Elasticserach的方法示例
Jun 04 Python
django 快速启动数据库客户端程序的方法示例
Aug 16 Python
Python函数的默认参数设计示例详解
Dec 01 Python
python实现简单飞行棋
Feb 06 Python
tensorflow转换ckpt为savermodel模型的实现
May 25 Python
Python 防止死锁的方法
Jul 29 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
自动跳转中英文页面
2006/10/09 PHP
php单件模式结合命令链模式使用说明
2008/09/07 PHP
php 验证码实例代码
2010/06/01 PHP
php curl 登录163邮箱并抓取邮箱好友列表的代码(经测试)
2011/04/07 PHP
windows服务器中检测PHP SSL是否开启以及开启SSL的方法
2014/04/25 PHP
64位windows系统下安装Memcache缓存
2015/12/06 PHP
jquery 可排列的表实现代码
2009/11/13 Javascript
javascript中length属性的探索
2011/07/31 Javascript
关于使用 jBox 对话框的提交不能弹出问题解决方法
2012/11/07 Javascript
Node.js 制作实时多人游戏框架
2015/01/08 Javascript
全面解析JS字符串和正则表达式中的match、replace、exec等函数
2016/07/01 Javascript
微信小程序 devtool隐藏的秘密
2017/01/21 Javascript
Vue结合SignalR实现前后端实时消息同步
2017/09/19 Javascript
JS实现520 表白简单代码
2018/05/21 Javascript
微信小程序支付前端源码
2018/08/29 Javascript
webpack dll打包重复问题优化的解决
2018/10/10 Javascript
基于vue实现图片验证码倒计时60s功能
2019/12/10 Javascript
微信小程序静默登录的实现代码
2020/01/08 Javascript
python简单实现旋转图片的方法
2015/05/30 Python
Python中elasticsearch插入和更新数据的实现方法
2018/04/01 Python
对python实现合并两个排序链表的方法详解
2019/01/23 Python
Python字符串处理的8招秘籍(小结)
2019/08/13 Python
keras 解决加载lstm+crf模型出错的问题
2020/06/10 Python
Python3爬虫关于代理池的维护详解
2020/07/30 Python
Jupyter notebook命令和编辑模式常用快捷键汇总
2020/11/17 Python
美国药妆网站:EDCskincare.com(防晒、痤疮、抗衰老等)
2017/04/28 全球购物
Nike西班牙官方网站:Nike.com (ES)
2017/10/30 全球购物
Lookfantastic瑞典:英国知名美妆购物网站
2018/04/06 全球购物
JPA的优势都有哪些
2013/07/04 面试题
手机业务员岗位职责
2013/12/13 职场文书
酒店大堂副理的职责范文
2014/02/13 职场文书
2015年办公室工作总结范文
2015/03/31 职场文书
运动会广播稿20字
2015/08/19 职场文书
《水上飞机》教学反思
2016/02/20 职场文书
Redis集群节点通信过程/原理流程分析
2022/03/18 Redis
CSS中calc(100%-100px)不加空格不生效
2023/05/07 HTML / CSS