هل تريد تشغيل سكربت بايثون تلقائيًا كل يوم أو ساعة، بدل فتح البرنامج وتشغيل الكود يدويًا في كل مرة؟ في هذا الدرس سنكتب أكوادًا جاهزة لتنفيذ مهمة كل عشر ثوانٍ، ثم كل ساعة أو يوم في وقت محدد باستخدام مكتبة schedule.
في الدرس 29 من سلسلة «بايثون بالمثال» لن نتوقف عند الجدولة داخل نافذة الطرفية؛ سنوضح أيضًا كيف تجعل Windows Task Scheduler أو cron في Linux يطلقان سكربتًا مستقلًا، وسنرفق مشروعًا عمليًا يحتفظ بسجل وقت كل تشغيل لتتحقق من النتائج بنفسك.
{alertInfo}المتطلبات: Python 3.9 أو أحدث، وتثبيتscheduleللأمثلة الدورية داخل بايثون فقط. برنامج تسجيل التنفيذ المستقل يستخدم مكتبات Python المدمجة ويمكن تشغيله دون تثبيتschedule.
حمّل أكواد الدرس الخمسة، وملفات إعداد Windows وLinux، والاختبارات؛ وجرّب تشغيل المهام تلقائيًا على جهازك.
تحميل ملفات الدرس ZIP{getToc} $title={محتوى المقال}
لماذا تحتاج إلى تشغيل سكربت بايثون تلقائيًا؟
قد تكتب سكربتًا يحدّث تقريرًا يوميًا، أو يسجل حالة مهمة كل ساعة، أو ينفّذ فحصًا دوريًا. بدل فتح الملف والضغط على «تشغيل» في كل مرة، يمكنك جدولة تشغيل سكربت بايثون تلقائيًا بحيث تنفذ المهمة في الموعد المطلوب.
سنستخدم مثالًا بسيطًا قابلًا للتحقق: يسجّل برنامج وقت تشغيله في ملف execution.log. لاحقًا سنشغّله كل عشر ثوانٍ داخل بايثون، ثم نجعله يُستدعى مرة يوميًا بواسطة نظام التشغيل. هذا يوضح الفرق بين الطريقتين دون الحاجة إلى حسابات خارجية أو اتصال بالإنترنت.
ما الفرق بين schedule وTask Scheduler وcron؟
| الأداة | متى تختارها؟ | هل يجب أن تبقى عملية بايثون مفتوحة؟ |
|---|---|---|
schedule | تنفيذ دالة مرارًا أثناء تشغيل برنامج بايثون نفسه | نعم؛ إنها جدولة داخل العملية الحالية |
Task Scheduler | بدء سكربت مستقل يوميًا أو في ساعة معينة على Windows | لا؛ نظام التشغيل يطلق العملية في الموعد |
cron | بدء سكربت مستقل وفق جدول على Linux | لا؛ خدمة cron تطلق العملية في الموعد |
{alertWarning}الفارق الحاسم: إغلاق عملية Python يوقف وظائفschedule. أما Windows Task Scheduler وcronفيمكنهما بدء تشغيل جديد للسكربت دون فتحه يدويًا. ومع ذلك يجب أن يكون الجهاز قيد التشغيل ومستوفيًا لشروط الجدولة؛ فلا توجد طريقة محلية لتشغيل مهمة على جهاز مطفأ تمامًا.
تثبيت مكتبة schedule وتجهيز المشروع
تحتاج إلى Python 3.9 أو أحدث في هذا الدرس. افتح الطرفية وتأكد من المفسّر المستخدم، ثم ثبّت المكتبة في البيئة نفسها:
python --version
python -m pip install schedule
python -m pip show schedule
مكتبة schedule صغيرة ومخصصة للجدولة داخل العملية، ويمكن الاطلاع على توثيقها الرسمي. أما ملف المهمة المستقلة الذي سنستخدمه مع نظام التشغيل فلا يحتاج إلى تثبيت أي مكتبة خارجية.
{alertWarning}تجنب تسمية ملفكschedule.py. ذلك قد يؤدي إلى استيراد ملفك بدل المكتبة، فتظهر أخطاء مثلAttributeError: module 'schedule' has no attribute 'every'عند تشغيل المثال.
أبسط كود: تشغيل دالة كل 10 ثوانٍ في بايثون
أنشئ ملفًا باسم first_schedule.py واكتب الكود التالي. اطبع وقت التنفيذ حتى ترى عمل الجدولة أمامك:
"""أول مثال: تشغيل وظيفة كل عشر ثوانٍ أثناء بقاء البرنامج مفتوحًا."""
import time
from datetime import datetime
import schedule
def job():
print("تنفيذ المهمة:", datetime.now().strftime("%H:%M:%S"), flush=True)
schedule.every(10).seconds.do(job)
print("بدأت الجدولة؛ اضغط Ctrl+C للإيقاف.")
try:
while True:
schedule.run_pending()
time.sleep(1)
except KeyboardInterrupt:
print("تم إيقاف الجدولة.")
شغّل الملف باستخدام الأمر التالي:
python first_schedule.py
بعد نحو عشر ثوانٍ ستظهر الرسالة الأولى، ثم تتكرر. مثال توضيحي على شكل المخرجات، وقد تختلف الأوقات الفعلية:
بدأت الجدولة؛ اضغط Ctrl+C للإيقاف.
تنفيذ المهمة: 09:30:10
تنفيذ المهمة: 09:30:20
تنفيذ المهمة: 09:30:30
{alertSuccess}لن تنفّذ المهمة فور تشغيل الملف. عندما تكتبevery(10).secondsفالتشغيل الأول يكون بعد الفترة المحددة تقريبًا. لإجراء تجربة فورية يمكنك استدعاء الدالةjob()مباشرة قبل دخول الحلقة.
شرح every وdo وrun_pending وsleep
| جزء الكود | مهمته |
|---|---|
schedule.every(10).seconds | إنشاء جدول بفاصل 10 ثوانٍ |
.do(job) | ربط الوظيفة المراد تنفيذها بالجدول |
schedule.run_pending() | تشغيل الوظائف التي حان موعدها في هذه اللحظة |
time.sleep(1) | تقليل استهلاك المعالج بين عمليات التحقق |
Ctrl+C | إيقاف الحلقة وإغلاق البرنامج |
الدالة run_pending() لا تعوّض جميع مرات التشغيل الفائتة. إذا كان البرنامج متوقفًا ساعة كاملة، فلا تتوقع تنفيذ المهمة كل دقيقة عن الماضي عند إعادة فتحه. كما أن الوظائف الطويلة قد تؤخر وظائف أخرى لأنها تُنفذ تسلسليًا في المثال البسيط.
تشغيل سكربت بايثون كل ساعة أو يوميًا في وقت محدد
الجدولة كل ساعة تستخدم every().hour، والجدولة اليومية عند التاسعة صباحًا تستخدم every().day.at("09:00"). المثال التالي يستعمل schedule.Scheduler() مستقلاً حتى تصبح إدارة مجموعة الوظائف أسهل:
"""مثال جدولة وظيفة كل ساعة ووظيفة يوميًا عند 09:00."""
import time
from datetime import datetime
import schedule
def show_message(label):
print(f"{datetime.now():%Y-%m-%d %H:%M:%S} | {label}", flush=True)
scheduler = schedule.Scheduler()
scheduler.every().hour.do(show_message, "المهمة التي تعمل كل ساعة")
scheduler.every().day.at("09:00").do(show_message, "المهمة اليومية")
for job in scheduler.jobs:
print("موعد التنفيذ القادم:", job.next_run)
try:
while True:
scheduler.run_pending()
time.sleep(1)
except KeyboardInterrupt:
print("تم إيقاف الجدولة.")
سيطبع البرنامج مواعيد التنفيذ القادمة الخاصة بكل وظيفة. وقد يختلف موعد التنفيذ اليومي بحسب توقيت الجهاز المحلي. إذا كانت الساعة التاسعة قد مرت اليوم، فسيكون الموعد التالي عادة في التاسعة من اليوم القادم ما دام البرنامج يعمل.
python daily_hourly.py
{alertInfo}هل تحتاج تشغيلًا عند التاسعة دون إبقاء Python مفتوحًا؟ انتقل إلى قسم Windows Task Scheduler أوcron. استخدام.day.at()وحده لا يجعل السكربت يعمل بعد إغلاقه.
تشغيل أكثر من مهمة وإلغاء جدول محدد
يمكنك تسجيل أكثر من وظيفة داخل Scheduler واحد، ووضع وسم لكل مجموعة حتى تُلغيها لاحقًا من خلال clear(). المثال التالي يطبع رسالتين بفاصلين مختلفين:
"""تجربة مهام متعددة مع إلغاء مهمة بالوسم."""
import time
import schedule
def work(label):
print("تم تنفيذ:", label, flush=True)
scheduler = schedule.Scheduler()
scheduler.every(5).seconds.do(work, "الأولى").tag("fast")
scheduler.every(10).seconds.do(work, "الثانية").tag("slow")
print("عدد المهام:", len(scheduler.jobs))
print("لإيقاف مجموعة المهام السريعة استخدم: scheduler.clear('fast')")
try:
while True:
scheduler.run_pending()
time.sleep(1)
except KeyboardInterrupt:
print("تم إيقاف التشغيل.")
عندما تحتاج لإلغاء المجموعة السريعة داخل برنامجك، نفّذ scheduler.clear("fast")؛ ستتوقف الوظائف التي تحمل هذا الوسم فقط. ويمكن أيضًا حفظ الوظيفة الناتجة من do() ثم إلغاؤها باستخدام scheduler.cancel_job(job).
مشروع عملي: سكربت مستقل يسجل وقت تشغيل المهمة
هذه الخطوة أهم من طباعة رسالة عابرة: سنكتب ملفًا باسم task_once.py ينفّذ مهمة مرة واحدة عند استدعائه، ويسجل الوقت في output/execution.log. ستستخدم أنظمة الجدولة هذا السكربت مباشرة، ولذلك لا يحتوي على حلقة while True.
"""مهمة مستقلة قابلة للاستدعاء يدويًا أو من Task Scheduler أو cron.
تكتب وقت التنفيذ في output/execution.log بجوار ملفات المشروع.
لا تحتاج إلى مكتبة schedule.
"""
import argparse
from datetime import datetime
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DEFAULT_LOG = BASE_DIR / "output" / "execution.log"
def write_log(message="تشغيل تلقائي", log_path=None):
"""اكتب سطرًا في سجل UTF-8 وأرجع المسار الناتج."""
if not isinstance(message, str) or not message.strip():
raise ValueError("نص المهمة يجب ألا يكون فارغًا")
path = Path(log_path) if log_path is not None else DEFAULT_LOG
path.parent.mkdir(parents=True, exist_ok=True)
clean_message = " ".join(message.splitlines()).strip()
timestamp = datetime.now().astimezone().isoformat(timespec="seconds")
with path.open("a", encoding="utf-8") as handle:
handle.write(f"{timestamp} | {clean_message}\n")
return path
def main(argv=None):
parser = argparse.ArgumentParser(description="حفظ وقت تنفيذ مهمة مجدولة")
parser.add_argument("--message", default="تشغيل تلقائي", help="نص يسجل مع وقت التنفيذ")
args = parser.parse_args(argv)
try:
path = write_log(args.message)
except (OSError, ValueError) as exc:
parser.exit(1, f"خطأ: {exc}\n")
print(f"تم التنفيذ وحفظ السجل: {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
جرّبه الآن دون جدولة:
python task_once.py --message "أول تشغيل"
سيُنشأ مجلد output بجوار السكربت تلقائيًا، وستجد سطرًا مشابهًا للآتي داخل execution.log:
2026-10-19T09:16:00+03:00 | أول تشغيل
هذا الناتج مثال توضيحي لشكل السجل وليس وقتًا ثابتًا في البرنامج؛ يكتب السكربت وقت جهازك الفعلي مع فرق التوقيت. استخدمنا Path(__file__).resolve().parent لتبقى ملفات السجل في مجلد المشروع حتى لو استُدعي السكربت من مجلد عمل آخر.
تجربة المشروع مع schedule وأمر تشغيل واحد
داخل حزمة الدرس ستجد أيضًا schedule_demo.py. يوفر خيارات سطر أوامر تجعل الاختبار أسهل، ويتصل بدالة تسجيل المهمة نفسها في task_once.py.
لإجراء ثلاثة تشغيلات تبدأ إحداها فورًا، ثم تتكرر كل عشر ثوانٍ، نفّذ:
python schedule_demo.py --every 10 --run-now --max-runs 3
ولتشغيل البرنامج كل يوم في التاسعة صباحًا ما دامت العملية مستمرة:
python schedule_demo.py --daily 09:00
يمكنك اختبار البرنامج على فترات أقصر باستخدام --every 1، أو إيقافه عبر Ctrl+C. ملف README.md المرفق يشرح كل المعاملات، ويحتوي المشروع على اختبارات وظائف التسجيل وإعداد خيارات الجدولة.
تشغيل سكربت بايثون يوميًا باستخدام Windows Task Scheduler
إذا أردت تشغيل السكربت عند ساعة محددة حتى بعد إغلاق نافذة CMD، فمن الأفضل أن ينشئ Windows عملية Python جديدة في الموعد، بدل إبقاء حلقة schedule في الطرفية.
- افتح Task Scheduler من قائمة ابدأ، ثم اختر Create Basic Task.
- اختر Daily وحدد وقتًا تجريبيًا مناسبًا، مثل 09:00.
- اختر Start a program ثم أدخل مسار المفسّر وملف المهمة كما هو موضح في الجدول.
- احفظ المهمة، ثم استخدم Run لاختبار تنفيذها فورًا.
- راجع ملف
output/execution.logالموجود في مجلد المشروع للتحقق من تسجيل وقت التنفيذ.
| الحقل داخل Task Scheduler | مثال يجب تعديله وفق جهازك |
|---|---|
Program/script | C:\Python312\python.exe |
Add arguments (optional) | "C:\PythonProjects\python-schedule-example-29\task_once.py" --message "Windows daily" |
Start in (optional) | C:\PythonProjects\python-schedule-example-29 |
هذه مسارات افتراضية للتوضيح فقط. اعرف المسار الحقيقي لمفسّر Python على جهازك باستخدام:
python -c "import sys; print(sys.executable)"
إذا كانت لديك بيئة افتراضية، استخدم ملف python.exe الخاص بها. اكتب المسار الصحيح إلى task_once.py بين علامتي اقتباس في الوسائط، واكتب Start in على شكل مسار مجلد دون علامات اقتباس. قد يتأثر التشغيل بصلاحيات الحساب، أو شروط الطاقة، أو تعطيل المهام التي تأخرت؛ راجع إعدادات المهمة وحقل Last Run Result عند تشخيص المشكلة.
يمكنك الرجوع إلى مراجع Microsoft Task Scheduler وإلى توثيق schtasks الرسمي لفهم الخيارات المتاحة على إصدارات Windows المختلفة.
تشغيل سكربت بايثون يوميًا في Linux باستخدام cron
في نظام Linux يمكن لخدمة cron إطلاق برنامج Python مستقل في أوقات محددة. اعرف مسار المفسّر أولًا، ثم افتح جدول المستخدم الحالي:
command -v python3
crontab -e
على سبيل المثال، يشغّل السطر التالي الملف يوميًا في الساعة 09:00 بحسب توقيت النظام المستخدم للجدولة. غيّر اسم المستخدم ومسارات الملفات قبل الحفظ:
0 9 * * * /usr/bin/python3 /home/USERNAME/python-schedule-example-29/task_once.py --message "Linux daily" >> /home/USERNAME/python-schedule-example-29/cron-output.log 2>&1
الرقم 0 يعني الدقيقة صفر، والرقم 9 يعني الساعة التاسعة. أما النجوم الثلاث الباقية فتعني كل يوم من الشهر، وكل شهر، وكل يوم من الأسبوع. سيُسجل task_once.py وقت التنفيذ داخل output/execution.log؛ ويحتفظ ملف cron-output.log برسائل التشغيل والأخطاء.
{alertWarning}مهم: استخدم المسار الصحيح للمفسّر، وخاصةً عند الاعتماد على بيئة افتراضية. ويجب أن يكون مجلد المشروع موجودًا قبل أول تشغيل حتى ينجح توجيه المخرجات إلىcron-output.log. كذلك لا تُشغّل ملفschedule_demo.pyكل دقيقة بواسطة cron، لأن ذلك قد ينشئ عمليات مستمرة متعددة؛ استخدمtask_once.pyبدلاً منه.
هل تعمل المهمة إذا أغلقت CMD أو أطفأت الكمبيوتر؟
| الحالة | النتيجة العامة |
|---|---|
أغلقت عملية Python التي تحتوي schedule | تتوقف الجدولة داخل تلك العملية |
| أغلقت نافذة CMD ثم انتظرت موعد مهمة Windows | يمكن للجدولة تشغيل سكربت جديد إذا كانت المهمة مضبوطة والشروط مستوفاة |
| أغلقت الطرفية ثم انتظرت مهمة cron | يمكن لـ cron إطلاق المهمة الجديدة إذا كانت خدمته تعمل |
| أطفأت الجهاز في وقت التنفيذ | لن ينفذ الجهاز المحلي برنامجًا وهو مطفأ؛ تختلف سياسة التعويض عند عودته حسب الأداة والإعدادات |
| أردت تنفيذًا مستمرًا طوال اليوم | تحتاج جهازًا أو خادمًا يظل متاحًا أو خدمة تشغيل مصممة لذلك |
أيضًا schedule ليست أداة ذات دقة زمنية صارمة أو ضمان تنفيذ كل موعد فائت. إذا كانت مهمتك تتعلق بمواعيد حرجة أو أحمال كبيرة، فاختر نظامًا أكثر ملاءمة للجدولة والمراقبة.
أخطاء شائعة عند جدولة وتشغيل سكربت بايثون
| الخطأ أو العرض | السبب الأقرب وما الذي تجربه؟ |
|---|---|
ModuleNotFoundError: No module named 'schedule' | ثبّت المكتبة في المفسّر نفسه عبر python -m pip install schedule وتحقق من البيئة الافتراضية. |
AttributeError: module 'schedule' has no attribute 'every' | تأكد أن اسم ملفك ليس schedule.py، ولا يوجد ملف آخر بهذا الاسم يحجب المكتبة. |
| المهمة لا تبدأ فورًا | تحدد every(10).seconds أول تشغيل بعد نحو عشر ثوانٍ؛ جرب --run-now في المشروع. |
| تتوقف المهمة بعد إغلاق الطرفية | كنت تعتمد على schedule داخل عملية انتهت؛ استخدم جدول النظام لتشغيل سكربت مستقل. |
| Task Scheduler يعمل دون إنشاء ملف السجل | تحقق من python.exe، والمسارات المطلقة، والحساب، وStart in، ونتيجة التنفيذ الأخيرة. |
| cron لا يعثر على المكتبات | غالبًا يستخدم مفسّرًا أو بيئة مختلفة؛ حدّد المسار المطلق لـ Python المطلوب. |
| اختلاف ساعة التنفيذ | تحقق من توقيت نظام التشغيل وإعدادات المنطقة الزمنية والتوقيت الصيفي. |
| لم تُنفذ مهام فائتة بعد عودة البرنامج | run_pending() لا يعيد تشغيل كل المواعيد التي فاتت أثناء توقف العملية. |
للمزيد راجع الأسئلة الشائعة الرسمية لمكتبة schedule وشرح أوامر بايثون الأساسية للمبتدئين لتقوية مهارات تشغيل الأكواد من الطرفية.
الأسئلة الشائعة عن تشغيل سكربت بايثون تلقائيًا
كيف أشغّل سكربت بايثون كل ساعة؟
إذا كان برنامج بايثون سيظل مفتوحًا، استخدم schedule.every().hour.do(job) مع حلقة run_pending(). وإذا أردت تشغيل سكربت مستقل من نظام التشغيل، فأنشئ جدولًا كل ساعة في Task Scheduler أو cron.
هل مكتبة schedule تعمل بدون إنترنت؟
نعم، بعدما تثبّتها، تعمل المكتبة محليًا دون الحاجة إلى الإنترنت لأداء الجدولة نفسها. لكن المهمة التي تكتبها قد تحتاج اتصالًا إذا كانت تتواصل مع موقع أو API.
هل يمكن تشغيل سكربت بايثون في وقت محدد والكمبيوتر مطفأ؟
لا؛ الجهاز المطفأ لا ينفذ البرامج. بعض إعدادات Windows أو أنظمة التشغيل قد تسمح بتشغيل المهمة عند الاستيقاظ أو بعد عودة الجهاز، لكن ذلك يختلف عن تنفيذها أثناء انقطاع التشغيل.
لماذا لا تعمل مهمة schedule بعد إغلاق نافذة CMD؟
لأن الوظائف المجدولة بهذا الأسلوب موجودة في ذاكرة عملية بايثون. إذا انتهت العملية انتهت الحلقة. استخدم مدير عمليات مناسبًا أو جدولة النظام حسب حاجتك.
ما الفرق بين schedule وcron؟
schedule ينفذ الدوال أثناء تشغيل برنامج بايثون؛ أما cron فيطلق برنامجًا مستقلًا وفق جدول يحدده نظام Linux.
ما الملفات التي سأحصل عليها في حزمة الدرس؟
ستجد first_schedule.py وdaily_hourly.py وschedule_demo.py وtask_once.py وmultiple_jobs.py، مع دليل الإعداد لويندوز ولينكس، وملف المتطلبات، واختبارات وحدات. لا تحتوي الحزمة على كلمات مرور أو رموز وصول.



