My website loads very slowly because the images are too large.
So, I thought about using Python to compress images.
Convert PNG files to WebP format – reduce file size by 88% directly!

The compression ratio is very high yet the image remains clear – a 1.4GB PNG file is compressed to just 120MB – and it's also in high resolution! Does anyone else have something like this?!
Python script functionality:
Recursive traversal
uploadsAll files in the directory.skip Files with a size of ≤ 300 KB.
Detect real image format(Identified by the file header, not the file extension); if the file is already in WebP format, it is skipped.
support PNG、JPG、JPEG、BMP、TIFF Convert images to WebP format, with quality set to ; 85(adjustable).
Keep the original file name and extension unchanged.(For example;
example.pngStill;example.pngHowever, the content is WebP).Back up the original file Copy to the specified backup directory (keep the complete relative path).
Skip non-image files (e.g., );
.zip、.pdf、.txtetc.).
Python code:
import os
import shutil
from PIL import Image
# ---------- 配置 ----------
SOURCE_DIR = r"Z:\path\to\your\uploads" # 替换为你的 uploads 目录路径
BACKUP_DIR = r"Z:\path\to\backup\uploads" # 备份目录(原文件移动到这里)
QUALITY = 85 # WebP 压缩质量 (1-100)
SIZE_THRESHOLD = 300 * 1024 # 300KB
# 支持的图片扩展名(即使扩展名不对,也会通过文件头检测)
IMAGE_EXTS = {'.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.tif', '.webp'}
# ---------- 核心函数 ----------
def is_webp(filepath):
"""通过文件头判断是否为 WebP 格式"""
with open(filepath, 'rb') as f:
header = f.read(12)
return header[:4] == b'RIFF' and header[8:12] == b'WEBP'
def compress_to_webp(src_path, dst_path, quality):
"""将图片压缩为 WebP 并保存到 dst_path(保持扩展名不变)"""
with Image.open(src_path) as img:
# 处理透明度:RGBA 保留,其他转 RGB
if img.mode in ('RGBA', 'LA', 'P'):
img = img.convert('RGBA')
else:
img = img.convert('RGB')
# 保存为 WebP,但文件名扩展名不变(浏览器根据文件头识别)
img.save(dst_path, 'WEBP', quality=quality, method=6)
# ---------- 主程序 ----------
def main():
if not os.path.exists(SOURCE_DIR):
print(f"❌ 源目录不存在:{SOURCE_DIR}")
return
os.makedirs(BACKUP_DIR, exist_ok=True)
total_files = 0
processed = 0
skipped_webp = 0
skipped_small = 0
skipped_nonimage = 0
errors = []
for root, dirs, files in os.walk(SOURCE_DIR):
for filename in files:
filepath = os.path.join(root, filename)
total_files += 1
# 1. 检查文件大小
if os.path.getsize(filepath) <= SIZE_THRESHOLD:
skipped_small += 1
continue
# 2. 检查是否为支持的图片格式(扩展名初步过滤)
ext = os.path.splitext(filename)[1].lower()
if ext not in IMAGE_EXTS:
skipped_nonimage += 1
continue
# 3. 真实格式检测(优先检测是否为 WebP)
try:
real_format = Image.open(filepath).format
except Exception:
# 无法打开则跳过
skipped_nonimage += 1
continue
# 如果已经是 WebP,跳过
if real_format == 'WEBP':
skipped_webp += 1
continue
# 4. 准备备份路径(保留相对路径)
rel_path = os.path.relpath(filepath, SOURCE_DIR)
backup_path = os.path.join(BACKUP_DIR, rel_path)
os.makedirs(os.path.dirname(backup_path), exist_ok=True)
# 5. 备份原文件(移动,而非复制,节省空间)
try:
shutil.move(filepath, backup_path)
except Exception as e:
errors.append(f"备份失败 {rel_path}: {e}")
continue
# 6. 压缩并保存到原位置(文件名不变)
try:
compress_to_webp(backup_path, filepath, QUALITY)
processed += 1
old_size = os.path.getsize(backup_path)
new_size = os.path.getsize(filepath)
print(f"✅ {rel_path} {old_size//1024}KB → {new_size//1024}KB ({new_size/old_size*100:.1f}%)")
except Exception as e:
# 压缩失败,尝试恢复原文件
shutil.move(backup_path, filepath)
errors.append(f"压缩失败 {rel_path}: {e}")
continue
# 统计报告
print("\n" + "="*60)
print(f" 处理完成!")
print(f" 总文件数:{total_files}")
print(f" 跳过(≤300KB):{skipped_small}")
print(f" 跳过(已是WebP):{skipped_webp}")
print(f" 跳过(非图片):{skipped_nonimage}")
print(f" 成功压缩:{processed}")
if errors:
print(f" 错误数量:{len(errors)}")
for err in errors:
print(f" ⚠️ {err}")
print("="*60)
if __name__ == "__main__":
main()