Featured image of post Tôi Đã Xây Dựng Một Công Cụ Để Chuyển Đổi 500+ Hình Ảnh Sang WebP Trong Một Giờ

Tôi Đã Xây Dựng Một Công Cụ Để Chuyển Đổi 500+ Hình Ảnh Sang WebP Trong Một Giờ

Điểm Lighthouse của tôi đang khóc vì những hình ảnh nặng. Vì vậy, tôi đã viết một ETL Python để chuyển đổi hàng loạt mọi thứ sang WebP và cập nhật tất cả các URL tự động. Đây là cách.

Tôi đang nhìn chằm chằm vào báo cáo Lighthouse của mình như thể nó nợ tôi tiền.

Hiệu suất: 62.

Thủ phạm? Hình ảnh. Hàng trăm hình ảnh rải rác trong các tệp markdown, được lưu trữ trên Flickr, Imgur, GitHub… tất cả ở định dạng JPEG và PNG chưa được tối ưu hóa.

Cách sửa thủ công sẽ là:

  1. Tải xuống từng hình ảnh
  2. Chuyển đổi sang WebP
  3. Tải lên CDN của tôi
  4. Tìm và thay thế mọi URL trong mọi tệp markdown

Với 500 hình ảnh duy nhất? Đó không phải là dự án cuối tuần. Đó là án tù.

Vì vậy, tôi đã làm điều mà bất kỳ kỹ sư lười biếng nào cũng làm: Tôi tự động hóa nó.

Vấn Đề

Blog của tôi sử dụng Hugo với các tệp markdown. Hình ảnh được tham chiếu ở khắp nơi:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Trong frontmatter
image: "https://live.staticflickr.com/65535/54519397357_403fc67f4a_k. jpg"

# Trong shortcodes gallery
< gallery id="example_id">
- https://live.staticflickr.com/65535/54525108024_adbff3cc9b_k. jpg
- https://live.staticflickr.com/65535/54520449879_784f0f24ca_k. jpg
< /gallery >

# Markdown tiêu chuẩn
![Ảnh của tôi](https://i.imgur.com/sXyG3GX. jpeg)

Mỗi hình ảnh phải được:

  • Tải xuống từ nguồn gốc
  • Chuyển đổi sang WebP (nhỏ hơn, nhanh hơn)
  • Tải lên CDN mới của tôi
  • URL được thay thế trong tệp markdown

Nhân với 500. Không cảm ơn.

Giải Pháp: Pipeline ETL

Tôi đã xây dựng bulk-webp-url-replacer—một công cụ Python làm chính xác những gì nó nói:

1
2
3
4
5
6
python -m bulk_webp_url_replacer \
  --scan-dir ./content \
  --download-dir ./downloads \
  --output-dir ./webp_images \
  --new-url-prefix "https://cdn.example.com/images" \
  --threads 8

Nó làm gì:

  1. Extract — Quét tất cả tệp .md để tìm URL hình ảnh (frontmatter, galleries, inline)
  2. Transform — Tải xuống từng hình ảnh và chuyển đổi sang WebP
  3. Load — Thay thế tất cả URL cũ bằng đường dẫn CDN mới

Một lệnh. 500 hình ảnh. Xong.

Các Chi Tiết Kỹ Thuật

Mẫu Regex Để Trích Xuất URL

Markdown có nhiều cách nhúng hình ảnh. Trình trích xuất của tôi xử lý tất cả:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PATTERNS = [
    # YAML frontmatter: image: "https://..."
    re.compile(r'^image:\s*["\']?(https?://[^"\'>\s]+)["\']?\s*$'),
    # TOML frontmatter: image = "https://..."
    re.compile(r'^image\s*=\s*["\']?(https?://[^"\'>\s]+)["\']?\s*$'),
    # Gallery shortcodes: - https://...
    re.compile(r'^\s*-\s+(https?://[^\s]+\.(jpg|jpeg|png|gif|webp))\s*$'),
    # Standard markdown: ![alt](https://...)
    re.compile(r'!\[[^\]]*\]\((https?://[^)]+)\)'),
]

Tải Xuống Song Song

Tải xuống 500 hình ảnh tuần tự? Chậm. Với ThreadPoolExecutor:

1
2
3
4
with ThreadPoolExecutor(max_workers=8) as executor:
    futures = {executor.submit(process_url, url): url for url in urls}
    for future in as_completed(futures):
        # Process results as they complete

8 luồng = nhanh hơn 8 lần. Toán học đơn giản.

Giới Hạn Tốc Độ & Thử Lại

Imgur không vui với sự nhiệt tình của tôi. Lỗi HTTP 429 khắp nơi.

Cách sửa: backoff theo cấp số nhân với headers giống trình duyệt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...',
    'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
}

for attempt in range(max_retries):
    response = requests.get(url, headers=HEADERS, timeout=30)
    if response.status_code == 429:
        time.sleep(2 ** attempt)  # 1s, 2s, 4s...
        continue

Bỏ Qua Thông Minh

Công cụ lưu mapping.json sau mỗi lần chạy:

1
2
3
{
  "https://old-url.com/image.jpg": "new-filename.webp"
}

Lần chạy tiếp theo? Nó bỏ qua các hình ảnh đã xử lý. Di chuyển gia tăng FTW.

Kết Quả

Trước:

  • 612 tham chiếu hình ảnh trên 72 tệp markdown
  • Hình ảnh rải rác trên Flickr, Imgur, GitHub
  • Lighthouse van xin lòng thương

Sau:

  • Tất cả hình ảnh được chuyển đổi sang WebP
  • Được lưu trữ trên một CDN duy nhất
  • URL được cập nhật tự động
  • Một giờ làm việc (chủ yếu xem thanh tiến trình)

Cải thiện hiệu suất:

  • Kích thước hình ảnh trung bình: nhỏ hơn 60-80%
  • Hiệu suất Lighthouse: 62 → 89

Bài Học Rút Ra

  1. Tự động hóa mở rộng. Những gì mất vài ngày thủ công chỉ mất một giờ để xây dựng và vài phút để chạy.

  2. Giới hạn tốc độ là thực tế. Luôn thêm thử lại và backoff. Các trang như Imgur sẽ hạn chế bạn.

  3. Chạy thử trước. Cờ --dry-run đã cứu tôi khỏi vô tình phá hỏng 72 tệp.

  4. WebP đáng giá. Chất lượng giống nhau, kích thước nhỏ hơn nhiều. Không có lý do gì để phục vụ JPEG vào năm 2026.

Hãy Tự Thử

Công cụ mã nguồn mở trên GitHub.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Xem trước những gì sẽ thay đổi
bulk-webp-url-replacer \
  --scan-dir ./content \
  --download-dir ./downloads \
  --output-dir ./webp \
  --dry-run

# Chạy thực sự
bulk-webp-url-replacer \
  --scan-dir ./content \
  --download-dir ./downloads \
  --output-dir ./webp \
  --new-url-prefix "https://your-cdn.com/images" \
  --threads 8

Điểm Lighthouse của bạn sẽ cảm ơn bạn. 🚀

Ví Dụ Đầu Ra

Sau khi chạy công cụ di chuyển, các URL được cập nhật tự động để trỏ đến các phiên bản WebP được tối ưu hóa:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Trong frontmatter
image: "https://raw.githubusercontent.com/HoangGeek/store/refs/heads/main/webp/54519397357_403fc67f4a_k.webp"

# Trong shortcodes gallery
< gallery id="example_id">
- https://raw.githubusercontent.com/HoangGeek/store/refs/heads/main/webp/54525108024_adbff3cc9b_k.webp
- https://raw.githubusercontent.com/HoangGeek/store/refs/heads/main/webp/54520449879_784f0f24ca_k.webp
< /gallery >

# Markdown tiêu chuẩn
![Ảnh của tôi](https://raw.githubusercontent.com/HoangGeek/store/refs/heads/main/webp/sXyG3GX.webp)
Được tạo với sự lười biếng tình yêu 🦥

Subscribe to My Newsletter