博主一般在发布文章时会将文章内包含的图片经过压缩优化之后上传以节省服务器硬盘空间,一直是使用TingPNG的web网页来处理图片。博主前两年心血来潮准备做图站的时候,收集了大约14万张图片,在web端处理比较耗费时间,那时候博主才注意到TinyPNG有不少官方认证的第三方工具,博主挑挑拣拣之后留下了TinyGUI这一个,通过TinyPNG的API,TinyGUI一次性可以处理N张图片。后来博主关闭了图站,但TinyGUI一直留着,图片少就直接在web端处理,图片多就用TinyGUI处理。直到今年初的时候,博主某一天突然觉得TinyGUI处理起来还不够简单,于是在TinyPNG官网找相关第三方工具,然后找到了Python脚本,啊,真香。

TinyPNG:https://tinypng.com
TinyPNG API:https://tinypng.com/developers
TinyGUI WIN 客户端:https://github.com/chenjing1294/TinyGUI
Python 项目地址:https://github.com/GcsSloop/TinyPng
Python 修复PR:https://github.com/GcsSloop/TinyPng/pull/5 (fix print function error in python3)

博主所用的设备相关介绍:
系统版本:Windows 10 IoT 企业版 LTSC (即2021 LTSC)
Python版本:3.11.0

Win10系统下TinyPNG Python脚本的用法很简单,将tinypng.py复制到需要压缩的图片的文件夹,在文件夹内打开CMD会话窗口,执行python tinypng.py就行,脚本会在当前文件夹内新建一个名为tiny的文件夹,脚本处理好的图片就在这个文件夹内(主要是博主懒得在脚本后面加参数设置文件夹路径)。至于Win10系统下Python的安装以及环境变量设置博主就不写了,自行谷歌百度两下。需要注意的是该python脚本需要调用TingPNG的API,在官网自行申请即可,脚本代码中有备注API填写位置。

脚本介绍:支持png/jpg/jpeg/webp后缀格式图片,加参数的用法详见脚本项目说明文档。脚本逻辑就是遍历目录下的png/jpg/jpeg/webp,然后上传到TinyPNG处理,之后下载到本地。
脚本代码:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import sys
import os.path
import click
import tinify

tinify.key = "7kWg8Tsr9vctq89sQGzFQ1Wv2Wmnd2RP"        # API KEY
version = "1.0.1"                # 版本

# 压缩的核心
def compress_core(inputFile, outputFile, img_width):
    source = tinify.from_file(inputFile)
    if img_width != -1:
        resized = source.resize(method = "scale", width  = img_width)
        resized.to_file(outputFile)
    else:
        source.to_file(outputFile)

# 压缩一个文件夹下的图片
def compress_path(path, width):
    print ("compress_path-------------------------------------")
    if not os.path.isdir(path):
        print ("这不是一个文件夹,请输入文件夹的正确路径!")
        return
    else:
        fromFilePath = path             # 源路径
        toFilePath = path+"/tiny"       # 输出路径
        print ("fromFilePath=%s" %fromFilePath)
        print ("toFilePath=%s" %toFilePath)

        for root, dirs, files in os.walk(fromFilePath):
            print ("root = %s" %root)
            print ("dirs = %s" %dirs)
            print ("files= %s" %files)
            for name in files:
                fileName, fileSuffix = os.path.splitext(name)
                if fileSuffix == '.png' or fileSuffix == '.jpg' or fileSuffix == '.jpeg' or fileSuffix == '.webp':
                    toFullPath = toFilePath + root[len(fromFilePath):]
                    toFullName = toFullPath + '/' + name
                    if os.path.isdir(toFullPath):
                        pass
                    else:
                        os.mkdir(toFullPath)
                    compress_core(root + '/' + name, toFullName, width)
            break                        # 仅遍历当前目录

# 仅压缩指定文件
def compress_file(inputFile, width):
    print ("compress_file-------------------------------------")
    if not os.path.isfile(inputFile):
        print ("这不是一个文件,请输入文件的正确路径!")
        return
    print ("file = %s" %inputFile)
    dirname  = os.path.dirname(inputFile)
    basename = os.path.basename(inputFile)
    fileName, fileSuffix = os.path.splitext(basename)
    if fileSuffix == '.png' or fileSuffix == '.jpg' or fileSuffix == '.jpeg' or fileSuffix == '.webp':
        compress_core(inputFile, dirname+"/tiny_"+basename, width)
    else:
        print ("不支持该文件类型!")

@click.command()
@click.option('-f', "--file",  type=str,  default=None,  help="单个文件压缩")
@click.option('-d', "--dir",   type=str,  default=None,  help="被压缩的文件夹")
@click.option('-w', "--width", type=int,  default=-1,    help="图片宽度,默认不变")
def run(file, dir, width):
    print ("GcsSloop TinyPng V%s" %(version))
    if file is not None:
        compress_file(file, width)              # 仅压缩一个文件
        pass
    elif dir is not None:
        compress_path(dir, width)               # 压缩指定目录下的文件
        pass
    else:
        compress_path(os.getcwd(), width)       # 压缩当前目录下的文件
    print ("结束!")

if __name__ == "__main__":
    run()

感谢脚本作者GcsSloop,感谢elfisworking修复python3报错。博主修改了一丢丢代码来支持webp后缀。

3 thoughts on “言小五折腾日记:Python脚本之Win10系统下使用TinyPNG的API批量压缩优化图片

  1. 轨迹说道:

    人还怪好勒,直接key也放上去给用。

    1. 言小五说道:

      @轨迹 haha,其实没想过放key来着,我把代码随便修了一下之后,测试能用就直接复制到文章里面了,懒得改了。

  2. jiyouzhan说道:

    这篇文章写得深入浅出,让我这个小白也看懂了!

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注