网站图太多、加载太慢,老板一句「把图片都压一压」——几百张 PNG/JPG 手动开 PS?不存在的。WebP 在同画质下通常只有 JPG 的 1/3 到 1/5 大小,且现代浏览器全支持。下面这份脚本本次完整实跑过:9 张测试图(含透明 PNG、P 模式 GIF、同名不同扩展的撞名图)转出 WebP,平均压到约 20%,透明通道 alpha 值逐像素核对保留。
#!/usr/bin/env python3
"""批量压缩图片并转 WebP:imgs/ -> out/,保留相对子目录结构。"""
import sys
from pathlib import Path
from PIL import Image
SRC = Path(sys.argv[1] if len(sys.argv) > 1 else "imgs")
DST = Path(sys.argv[2] if len(sys.argv) > 2 else "out")
QUALITY = int(sys.argv[3]) if len(sys.argv) > 3 else 80
EXTS = {".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tif", ".tiff"}
total_in = total_out = n = 0
skipped = []
for p in sorted(SRC.rglob("*")):
if not p.is_file() or p.suffix.lower() not in EXTS:
continue
rel = p.relative_to(SRC)
out_path = (DST / rel).with_suffix(".webp")
out_path.parent.mkdir(parents=True, exist_ok=True)
try:
im = Image.open(p)
im.seek(0) # 多帧 GIF 只取首帧
im.save(out_path, "WEBP", quality=QUALITY, method=6)
except Exception as e:
skipped.append(f"{rel}: {e}")
continue
i, o = p.stat().st_size, out_path.stat().st_size
total_in += i; total_out += o; n += 1
print(f"{rel} {i//1024}KB -> {o//1024}KB ({o*100//max(i,1)}%)")
print(f"\n{n} 张,合计 {total_in//1024}KB -> {total_out//1024}KB"
f"(压缩到 {total_out*100//max(total_in,1)}%)")
for s in skipped:
print("失败/跳过:", s)
用法:python3 webp_batch.py 输入目录 输出目录 80(80 是质量,可 70~85 之间调)。依赖就一个:pip install pillow。
a.jpg 9KB -> 1KB (11%) a.png 2KB -> 1KB (36%) big.jpg 92KB -> 10KB (11%) # 3000x2000 大图 big.png 21KB -> 10KB (48%) pal.gif ~0KB -> ~0KB 9 张,合计 154KB -> 34KB(压缩到 21%)
透明 PNG 转 RGBA WebP 后逐像素验证:alpha=128 的半透明像素原样保留((0,255,1,128)),纯色图转出是 RGB,正常。
dup.png 和 dup.jpg 是两张内容不同的图,转完都叫 dup.webp——后处理的那张悄悄覆盖前一张,不报任何错。实跑验证:转出后读像素,发现只剩其中一张的内容。几百张图里混进几张撞名的,丢图了你都不知道。修法:转出前检查目标是否存在,存在就在文件名里加后缀(如 dup_2.webp),或把整目录先按「去扩展名重命名」预检一遍。Image.open(t.png).save(x.jpg) 带 alpha 的图直接抛 OSError「cannot write mode RGBA as JPEG」。转 WebP 没这个问题(WebP 支持 alpha),但如果要 JPG 得先 im.convert('RGB')。im.seek(0) 时帧游标在末尾,部分版本保存会异常;另外脚本转出来的是静态图,动画 GIF 的动画会丢——要保动画就别转它,或者明确知道只要首帧。if o >= i: out_path.unlink() # 不划算就删掉,保留原图。method=6 压得最小但最慢,图多时可以降到 4;