Fri Jun 26 2026 20:00:00 GMT-0400 (北美东部夏令时间)

ضغط الصور في Python باستخدام Pillow وWebP وmozjpeg

اضغط صور JPEG وPNG وWebP في بايثون باستخدام Pillow وcwebp وmozjpeg. يتضمن الدليل إعدادات الجودة، وحلقات تحديد الحجم المستهدف، وسكريبت دفعي سريع.

ضغط الصور في Python باستخدام Pillow وWebP وmozjpeg

آخر تحديث: June 27, 2026

لديك مجلد من صور JPEG بحجم الكاميرا، وخطوة بناء ترسلها إلى المستخدمين، وميزانية لوزن الصفحة تتسبب في التضخم المستمر. لا يمكن ضغط هذه الملفات يدوياً باستخدام محرر الصور؛ فهذا غير قابل للتوسع. يرشدك هذا الدليل إلى كيفية استخدام Python بالضبط لتصغير ملفات JPEG و PNG و WebP وفق جدول زمني، أو ضمن مهمة CI، أو داخل معالج تحميل.

إجابة سريعة: كيف تضغط الصور في Python؟

افتح الصورة باستخدام Pillow، ثم احفظها بأعلام التنسيق الصحيحة. بالنسبة للصور الفوتوغرافية، قم بالحفظ كـ JPEG باستخدام quality=80 و optimize=True و progressive=True. للحصول على أصغر الملفات الحديثة، احفظها كـ WebP باستخدام quality=80, method=6. للرسومات المسطحة ولقطات الشاشة، احتفظ بـ PNG وأمرر optimize=True.

from PIL import Image

def compress_jpeg(src, dst, quality=80):
    img = Image.open(src)
    if img.mode in ("RGBA", "P"):
        img = img.convert("RGB")
    img.save(dst, "JPEG", quality=quality, optimize=True, progressive=True)

هذه الدالة المكونة من أربعة أسطر تقلل بالفعل معظم صور JPEG الملتقطة بالهواتف والكاميرات الاحترافية بنسبة تتراوح بين 40% و 70%. يضيف بقية هذا الدليل أهداف الحجم، و WebP، و mozjpeg، ومُشغّل دفعات (batch runner) حتى تتمكن من توجيهه إلى دليل والمضي في عملك.

ما الذي تحتاجه قبل أن تبدأ؟

أنت بحاجة إلى Python 3.9 أو أحدث و Pillow، وهو التفرع المُدار لـ PIL. يتعامل Pillow مع JPEG و PNG و WebP بشكل افتراضي على كل منصة شائعة.

pip install Pillow

للحصول على أصغر مخرجات، ستحتاج أيضًا إلى مُشفّرين يعملان عبر سطر الأوامر يمكن لـ Python استدعاؤهما: cwebp من libwebp و cjpeg من mozjpeg.

brew install webp mozjpeg   # macOS؛ Linux: apt install webp, build mozjpeg

تحقق مما هو الغرض من كل قطعة قبل كتابة الكود:

  • Pillow يقرأ ويكتب كل التنسيقات ويقوم بتغيير الحجم.
  • cwebp ينتج أصغر ملفات WebP ويوفر خيارات ضبط أكثر من Pillow.
  • mozjpeg يعيد تشفير صور JPEG الأصغر من libjpeg القياسي الذي يستخدمه Pillow.
  • مساعد إعداد التقارير (A reporting helper) بحيث تطبع كل عملية كم عدد البايتات التي وفرتها.

إليك المساعد المستخدم في جميع أنحاء هذا الدليل:

import os

def report(src, dst):
    before, after = os.path.getsize(src), os.path.getsize(dst)
    saved = (1 - after / before) * 100
    print(f"{before/1024:.0f} KB -> {after/1024:.0f} KB ({saved:.0f}% smaller)")

إذا كنت تحتاج فقط إلى ضغط لمرة واحدة ولا تريد تثبيت أي شيء، فإن أداة ضغط الصور المستندة إلى المتصفح تقوم بنفس العمل لعدد قليل من الملفات.

كيف تضغط JPEG باستخدام Pillow؟

الأعلام الثلاثة المهمة لـ JPEG هي quality و optimize و progressive. يتحكم quality في مقدار التفاصيل التي يتخلص منها المُشفّر. يقوم optimize=True بتشغيل مرور ثانٍ لبناء جدول Huffman أصغر. يعيد progressive=True ترتيب الملف بحيث يرسم من الأعلى إلى الأسفل بدقة منخفضة أولاً، مما يجعله يبدو أسرع على الاتصالات البطيئة ويصغّر حجم الملف عادةً أكثر قليلاً.

جهاز كمبيوتر محمول على مكتب خشبي يُظهر كود المصدر مفتوحًا في المحرر

اختر قيمة جودة بناءً على المكان الذي ستُستخدم فيه الصورة، وليس رقماً عالمياً واحداً:

Quality value Typical use What to expect
90-95 Hero images, print proofs Near-original, noticeably larger files
75-85 Most web photos The safe default, large savings
60-70 Thumbnails and previews Visible softening on fine detail
Below 50 Avoid for photos Blocky artifacts around edges

هناك فخ يقع فيه الجميع تقريباً: لا يحتوي JPEG على قناة ألفا (alpha channel). إذا قمت بفتح لقطة شاشة PNG بشفافية وحفظها مباشرة كـ JPEG، فإن Pillow ترفع خطأ cannot write mode RGBA as JPEG. يقوم الاستدعاء convert("RGB") في الدالة السابقة بتسطيح الشفافية على خلفية صلبة أولاً، ولهذا السبب يتم تشغيله قبل كل عملية حفظ لملف JPEG.

ضغط PNG والتحويل إلى WebP

PNG هو تنسيق غير مُفقِد للبيانات (lossless)، لذلك لا يمكنك مقايضة الجودة بالحجم بالطريقة التي تفعلها مع JPEG. ما يمكنك فعله هو تشغيل مُحسّن Pillow، وبالنسبة لأي شيء هو في الواقع صورة فوتوغرافية، قم بتغيير التنسيق بالكامل.

def compress_png(src, dst):
    Image.open(src).save(dst, "PNG", optimize=True, compress_level=9)

def to_webp(src, dst, quality=80):
    img = Image.open(src)
    img.save(dst, "WEBP", quality=quality, method=6)

يخبرك method=6 مُشفّر WebP بقضاء المزيد من الوقت في البحث عن ملف أصغر. إنه أبطأ لكل صورة ولكنه يستحق ذلك في خطوة البناء. بالنسبة لصورة بدأت كملف PNG بحجم 2 MB، فإن WebP بجودة 80 يصل بشكل روتيني إلى أقل من 200 KB بينما يبدو مطابقاً للحجم الطبيعي للمشاهدة. تضع قياسات Google نفسها أن WebP أصغر بنحو 25% إلى 35% من JPEG المماثل، وفقًا لـ WebP documentation.

استخدم هذا الجدول لاختيار التنسيق بدلاً من التخمين:

Source content Best format Why
Photograph WebP or JPEG Smooth gradients compress well lossy
Logo, icon, flat UI PNG or WebP lossless Sharp edges stay crisp
Screenshot with text PNG or lossless WebP Avoids fuzzy text from lossy passes
Image needing transparency WebP or PNG JPEG cannot store an alpha channel

إذا كنت لا تزال تقرر بين التنسيقات لموقع كامل، فإن التفصيل في WebP vs JPEG vs PNG يغطي دعم المتصفح والمقايضات بمزيد من العمق.

الوصول إلى حجم ملف مستهدف ضمن حلقة (loop)

"اجعله أقل من 200 KB" هو مطلب شائع من أدوات البريد الإلكتروني، وأسواق المتاجر، وحدود تحميل CMS. لا يمكنك اختيار رقم جودة يصل إلى هدف البايتات في المحاولة الأولى، لأن نفس الجودة تنتج أحجاماً مختلفة تماماً لصورة مزدحمة مقابل صورة مسطحة. قم بالمرور تنازلياً من جودة عالية حتى تصل إلى الحد الأقصى:

def compress_to_target(src, dst, target_kb=200, q=90, floor=35):
    img = Image.open(src).convert("RGB")
    while q >= floor:
        img.save(dst, "JPEG", quality=q, optimize=True, progressive=True)
        if os.path.getsize(dst) <= target_kb * 1024:
            return q
        q -= 5
    return q

توقف وسيط floor عن الحلقة قبل أن تنخفض الجودة إلى منطقة التلطيخ (smear territory). إذا لم تتمكن الصورة من الوصول إلى الهدف فوق الحد الأدنى، فإن الدالة لا تزال تُرجع القيمة حتى تتمكن من تسجيلها وتغيير حجم الصورة بدلاً من الضغط بقوة أكبر. بالنسبة لميزانيات البايت الثابتة مثل 100 KB أو 50 KB، يعمل نفس التكرار؛ ويشرح الشرح التفصيلي في compress images without losing quality متى يتفوق تغيير الحجم على خفض الجودة.

كيف تضغط صور JPEG أصغر باستخدام mozjpeg و cwebp؟

يحفظ Pillow صور JPEG باستخدام مُشفّر libjpeg القياسي. mozjpeg هو مُشفّر جاهز من Mozilla ينتج ملفات أصغر بنفس الجودة البصرية، وعادةً ما يكون أقل بنسبة 5% إلى 15%. نظرًا لأن Pillow لا تقوم بتضمينه، فاستدعِ الثنائي cjpeg عبر subprocess. يقرأ cjpeg الخاص بـ mozjpeg ملفات PPM بشكل موثوق، لذا قم بتسطيح الصورة باستخدام Pillow أولاً:

import subprocess

def mozjpeg(src, dst, quality=80):
    Image.open(src).convert("RGB").save("_tmp.ppm")
    subprocess.run(["cjpeg", "-quality", str(quality), "-progressive",
                    "-optimize", "-outfile", dst, "_tmp.ppm"], check=True)
    os.remove("_tmp.ppm")

بالنسبة لـ WebP، فإن استدعاء cwebp مباشرة يمنحك خيارات لا تكشف عنها Pillow، مثل -m 6 لأقصى جهد و -sharp_yuv لحواف الألوان الأنظف:

def cwebp(src, dst, quality=82):
    subprocess.run(["cwebp", "-q", str(quality), "-m", "6",
                    "-sharp_yuv", src, "-o", dst], check=True)

إليك كيف تتراصف الخيارات حتى تتمكن من تحديد ما يجب تثبيته:

Tool Best for Trade-off
Pillow General scripting, every format Not the smallest JPEG
mozjpeg Smallest progressive JPEG Extra binary, PPM round-trip
cwebp (libwebp) Tightest WebP control Separate binary to install
pillow-simd Faster resizing on x86 servers Harder to build and pin

توثق مجموعة mozjpeg المُشفّر الخاص بها ومقارناتها في mozjpeg repository، وتعيش القائمة الكاملة لخيارات حفظ Pillow في Pillow image file formats reference.

كيف تضغط مجلد بالكامل بسرعة؟

الصورة الواحدة سهلة. المهمة الحقيقية هي دليل يحتوي على مئات الصور. قم بتغيير حجم أي شيء أعرض من احتياجات تخطيطك، ثم اضغط، وقم بتشغيل العمل عبر الخيوط (threads) حتى يكتمل مجلد صور بحجم 500 صورة في ثوانٍ بدلاً من دقائق.

مطور في محطة عمل متعددة الشاشات يشغل الكود أثناء ارتدائه سماعات الرأس

from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def batch(in_dir, out_dir, quality=80, max_width=1920):
    out = Path(out_dir); out.mkdir(parents=True, exist_ok=True)
    files = [p for p in Path(in_dir).iterdir()
             if p.suffix.lower() in {".jpg", ".jpeg", ".png"}]
    def work(p):
        img = Image.open(p)
        if img.width > max_width:
            h = round(img.height * max_width / img.width)
            img = img.resize((max_width, h), Image.LANCZOS)
        img.convert("RGB").save(out / f"{p.stem}.jpg", "JPEG",
                                quality=quality, optimize=True, progressive=True)
    with ThreadPoolExecutor() as pool:
        list(pool.map(work, files))
    return len(files)

بعض الملاحظات العملية من تشغيل هذا في بيئة الإنتاج:

  1. قم بتغيير الحجم قبل الضغط. عرض صورة بحجم 6000 px لعمود بحجم 1200 px يهدر بايتات لا يمكن لأي إعداد جودة استعادتها.
  2. يطلق Pillow قفل GIL أثناء الترميز، لذا فإن الخيوط تمنح تسريعًا حقيقيًا هنا؛ ولست بحاجة إلى multiprocessing لمعظم المجلدات.
  3. اكتب في دليل إخراج منفصل حتى لا يؤدي التشغيل السيئ أبدًا إلى الكتابة فوق ملفاتك الأصلية.
  4. استخدم Image.LANCZOS لتصغير الحجم؛ فهو يحافظ على حواف أكثر حدة من الفلتر الافتراضي.
  5. سجل كل ملف باستخدام مساعد report واحتفظ بالأصول حتى تتفحص عينة.

إذا كانت مهمتك متكررة وليست لمرة واحدة، فإن الأنماط الموجودة في batch image processing تغطي قوائم الانتظار والمحاولات وأساليب التسمية. بالنسبة لتشغيل بلا كود على ملف مضغوط (zip) من الملفات، تتعامل batch tool و image converter مع تغيير التنسيق دون الحاجة إلى سكريبت.

أي نهج يجب أن تختاره؟

طابق الطريقة بالوظيفة بدلاً من الوصول دائمًا إلى الأداة الأثقل:

  • صور الويب في خطوة البناء: WebP بواسطة Pillow بجودة 80، أو cwebp باستخدام -m 6 عندما تريد النسب المئوية القليلة الأخيرة.
  • ميزانية بايت صارمة: حلقة compress_to_target، مع التراجع إلى تغيير الحجم عند الوصول إلى الحد الأدنى.
  • خطوط أنابيب JPEG القديمة التي لا يمكنك تغييرها: أعد تشفير المخرجات باستخدام mozjpeg لتوفير مجاني.
  • لقطات الشاشة والرسوم البيانية: احتفظ بـ PNG باستخدام optimize=True، أو WebP غير مُفقِد للبيانات إذا كانت متصفحاتك تدعمه.

بالنسبة للنظرية وراء سبب عمل هذه الإعدادات، يشرح image compression guide التباين بين الضغط المُفقِد والمُتساوي وكيف تؤثر جداول التكميم على النتيجة.

الأخطاء الشائعة والتصحيحات

تسبب حفنة من الاستثناءات معظم عمليات التشغيل الفاشلة:

  • cannot write mode RGBA as JPEG — استدعِ img.convert("RGB") قبل الحفظ كـ JPEG.
  • Image.DecompressionBombError — يقوم Pillow بحماية ضد الصور الضخمة؛ ارفع الحد فقط للملفات التي تثق بها باستخدام Image.MAX_IMAGE_PIXELS = 200_000_000.
  • OSError: broken data stream — الملف المصدر مُقتطع؛ اضبط ImageFile.LOAD_TRUNCATED_IMAGES = True لإنقاذه، ثم أعد تنزيله عند الإمكان.
  • المخرج أكبر من المدخل — قمت بإعادة حفظ JPEG مضغوط بالفعل؛ قم بالضغط من الأصل، وليس من تصدير سابق.
  • تبدو الألوان باهتة — تم حفظ مصدر CMYK دون تحويل؛ استدعِ convert("RGB") أولاً.
from PIL import Image, ImageFile
Image.MAX_IMAGE_PIXELS = 200_000_000
ImageFile.LOAD_TRUNCATED_IMAGES = True

صورة مقربة ومفصلة لـ python مستلقٍ على سطح مثقوب.

الخلاصة الرئيسية

يتعامل Pillow مع 90% من ضغط الصور في Python باستخدام ثلاثة أعلام JPEG وعملية حفظ WebP. استخدم mozjpeg و cwebp عندما تحتاج إلى أصغر الملفات الممكنة، وقم بتغليف كل شيء في حلقة حجم الهدف عندما تكون ميزانية البايت ثابتة، ومرر مجلداً عبر دالة الدفعات الخيطية (threaded batch function) عندما ينمو الحجم. احتفظ بأصولك، وغير الحجم قبل الضغط، واطبع التوفير في كل عملية تشغيل حتى تعرف دائماً ما الذي يقوم به خط الأنابيب بالفعل.

استخدم الأدوات المجانية أثناء متابعة الدليل.

صورة غلاف PNG to WebP: دليل تحويل وضغط صور PNG

Tue Mar 17 2026 20:00:00 GMT-0400 (北美东部夏令时间)

PNG to WebP: دليل تحويل وضغط صور PNG

حوّل PNG إلى WebP للحصول على ملفات ويب أصغر وأكثر كفاءة. نستعرض متى يتفوق WebP غير المفقود ومتى يكون التشويه مقبولاً، بالإضافة إلى الأحجام الفعلية وأوامر cwebp و Pillow مع دعم احتياطي لـ PNG.