أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

أتمتة ترتيب Downloads بـ Python بدون حذف ملف بالغلط

📅 ٢٥ أبريل ٢٠٢٦⏱ 4 دقائق قراءة
أتمتة ترتيب Downloads بـ Python بدون حذف ملف بالغلط

أتمتة ترتيب Downloads بـ Python بدون حذف ملف بالغلط

هتطلع من المقال بسكربت آمن يرتب مجلد Downloads تلقائيًا، ويقلل تنظيف أسبوعي من 45 دقيقة تقريبًا إلى 6 دقائق متابعة فقط.

مستوى القارئ: مبتدئ إلى متوسط

المشكلة باختصار

اللي بيحصل فعلاً إن Downloads بيجمع كل شيء: فواتير PDF، صور WhatsApp Web، ملفات ZIP، exports من أدوات SaaS، وملفات كود مؤقتة. بعد أسبوعين بتلاقي 300 ملف، وتبدأ تنقل يدويًا. الطريقة دي بتفشل لأنها تعتمد على مزاجك، وممكن تنقل ملف مهم أو تحذف ملف لسه محتاجه.

الافتراض إنك على Windows، وعندك Python 3.10 أو أحدث، ومجلد Downloads فيه أقل من 10 آلاف ملف. لو عندك أكثر من كده، نفس الفكرة تشتغل، لكن هتحتاج تقسيم وتشغيل أهدأ.

مخطط يوضح تدفق أوتوميشن ترتيب مجلد Downloads من الفحص إلى النقل الآمن والتسجيل

الفكرة: انقل، لا تحذف

ركز: أول نسخة من أي أوتوميشن ملفات لازم تكون دفاعية. يعني تعمل dry-run الأول، تكتب log، وتنقل الملفات بدل ما تحذفها. مثال بسيط: ملف invoice.pdf يروح إلى Downloads/Sorted/Documents، وصورة screenshot.png تروح إلى Downloads/Sorted/Images. لو اسم الملف موجود، السكربت يضيف رقم بدل ما يستبدله.

الـ trade-off هنا واضح. هتكسب ترتيب يومي ثابت ومخاطر أقل. هتخسر شوية مرونة، لأن التصنيف مبني على الامتداد وليس على محتوى الملف. ده مناسب كبداية، وبعدها ممكن تضيف OCR أو قواعد حسب الاسم.

السكربت العملي

اعمل ملف اسمه sort_downloads.py. شغّله أول مرة بـ --dry-run. لو المخرجات منطقية، شيله في التشغيل المجدول.

Python
from pathlib import Path
import argparse
import shutil
import logging

RULES = {
    "Documents": {".pdf", ".docx", ".xlsx", ".csv", ".txt"},
    "Images": {".png", ".jpg", ".jpeg", ".webp", ".gif"},
    "Archives": {".zip", ".rar", ".7z", ".tar", ".gz"},
    "Code": {".py", ".js", ".ts", ".json", ".sql"},
}

def category_for(path: Path) -> str:
    suffix = path.suffix.lower()
    for category, extensions in RULES.items():
        if suffix in extensions:
            return category
    return "Other"

def unique_target(target: Path) -> Path:
    if not target.exists():
        return target
    stem, suffix = target.stem, target.suffix
    for i in range(1, 1000):
        candidate = target.with_name(f"{stem}-{i}{suffix}")
        if not candidate.exists():
            return candidate
    raise RuntimeError(f"Too many duplicates for {target.name}")

def sort_downloads(downloads: Path, dry_run: bool) -> None:
    sorted_root = downloads / "Sorted"
    log_file = sorted_root / "sort-downloads.log"
    sorted_root.mkdir(exist_ok=True)

    logging.basicConfig(
        filename=log_file,
        level=logging.INFO,
        format="%(asctime)s %(levelname)s %(message)s",
    )

    for item in downloads.iterdir():
        if item.name == "Sorted" or item.is_dir():
            continue
        category = category_for(item)
        target_dir = sorted_root / category
        target = unique_target(target_dir / item.name)
        message = f"{item.name} -> {target.relative_to(downloads)}"
        print("DRY" if dry_run else "MOVE", message)
        logging.info(("DRY " if dry_run else "MOVE ") + message)
        if not dry_run:
            target_dir.mkdir(parents=True, exist_ok=True)
            shutil.move(str(item), str(target))

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--downloads", default=str(Path.home() / "Downloads"))
    parser.add_argument("--dry-run", action="store_true")
    args = parser.parse_args()
    sort_downloads(Path(args.downloads), args.dry_run)

اختبره كده:

PowerShell
python .\sort_downloads.py --dry-run
python .\sort_downloads.py

تشغيله يوميًا على Windows

بدل ما تفتكر تشغله، استخدم Task Scheduler من خلال schtasks. الأمر التالي يشغله كل يوم الساعة 8 مساءً. عدّل مسار Python ومسار السكربت حسب جهازك.

PowerShell
schtasks /Create /SC DAILY /ST 20:00 /TN "SortDownloads" /TR "python C:\Users\ahmed\Scripts\sort_downloads.py"

لو عندك 200 ملف جديد أسبوعيًا، التنظيف اليدوي ممكن ياخد 30 إلى 45 دقيقة. بعد التشغيل اليومي، غالبًا هتحتاج 5 إلى 6 دقائق أسبوعيًا تراجع مجلد Other والـ log. الرقم تقديري، لكنه قابل للقياس من وقتك الفعلي قبل وبعد.

رسم أعمدة يقارن وقت ترتيب مجلد Downloads يدويًا مقابل أوتوميشن يومي

الـ trade-offs وما يجب الانتباه له

  • الاعتماد على الامتداد: سريع وبسيط، لكنه لا يعرف إن ملف PDF فاتورة أو عقد. لو عايزها تدعم فهم المحتوى، ضيف OCR أو قواعد بالاسم لاحقًا.
  • النقل بدل الحذف: أكثر أمانًا، لكنه لا يوفر مساحة تخزين كبيرة. المكسب هنا تنظيم وتقليل أخطاء، مش تنظيف disk عميق.
  • التشغيل اليومي: يقلل الفوضى تدريجيًا، لكنه ممكن ينقل ملف أنت لسه بتحمله لو اتشغل في وقت غلط. اختار وقتًا بعيدًا عن شغلك المعتاد.

متى لا تستخدم هذه الطريقة

لا تستخدمها على مجلد مشترك بين فريق بدون مراجعة صلاحيات وسياسة أسماء واضحة. لا تستخدمها لو عندك workflow يعتمد على بقاء الملفات في جذر Downloads. ولا تبدأ بدون --dry-run، لأن أول تشغيل هو أهم نقطة لكشف قواعد التصنيف الغلط.

مصادر اعتمد عليها المقال

  • توثيق Python pathlib للتعامل مع المسارات بطريقة آمنة.
  • توثيق Python shutil لاستخدام shutil.move في نقل الملفات.
  • توثيق Microsoft schtasks /Create لتشغيل المهمة يوميًا.

الخطوة التالية

شغّل السكربت بـ --dry-run على Downloads الحقيقي، واقرأ أول 20 سطر من المخرجات. لو لقيت ملفًا واحدًا رايح لمكان غلط، عدّل RULES قبل ما تشغّل النقل الفعلي.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة