الفصل الثاني عشر: المكتبات الخارجية والبيئات الافتراضية
مقدمة الفصل
تخيل أنك تبني منزلاً. بايثون، كلغة، تعطيك الأدوات الأساسية: مطرقة، منشار، ومسامير (أي، if
, for
, def
). يمكنك بناء هيكل المنزل بهذه الأدوات. لكن ماذا عن السباكة، الكهرباء، أو طلاء الجدران؟ هل يجب عليك صناعة كل الأنابيب والأسلاك والدهانات من الصفر؟ بالطبع لا.
هنا يأتي دور المكتبات الخارجية (External Libraries). إنها مثل متجر ضخم للأدوات والتجهيزات الجاهزة التي صنعها مبرمجون آخرون حول العالم. تحتاج إلى أدوات للتواصل مع الإنترنت؟ هناك مكتبة لذلك. تحتاج إلى أدوات لتحليل البيانات الضخمة؟ هناك مكتبة لذلك.
لكن قبل أن نبدأ بتثبيت هذه الأدوات، يجب أن نتعلم أولاً كيف نحافظ على تنظيم مشاريعنا. عندما تعمل على مشاريع مختلفة، قد يحتاج كل مشروع إلى إصدارات مختلفة من نفس المكتبة. الحل يكمن في البيئات الافتراضية (Virtual Environments).
في هذا الفصل، سنتعلم الممارسة الاحترافية لإدارة المشاريع:
- إنشاء بيئات معزولة لكل مشروع.
- استخدام مدير الحزم
pip
لتثبيت المكتبات داخل هذه البيئات. - استكشاف بعض المكتبات الخارجية التي ستغير طريقة برمجتك.
1. البيئات الافتراضية: تنظيم مساحة عملك
تخيل أنك تعمل على مشروعين:
- المشروع أ: يتطلب مكتبة تحليل بيانات بالإصدار 1.0.
- المشروع ب: هو مشروع أحدث ويتطلب نفس المكتبة ولكن بالإصدار 2.0.
إذا قمت بتثبيت المكتبات على نظامك مباشرة، فستواجه مشكلة. لا يمكنك تثبيت إصدارين مختلفين من نفس المكتبة في نفس الوقت.
الحل هو البيئة الافتراضية. البيئة الافتراضية هي نسخة معزولة ومستقلة من بايثون ومكتباتها، خاصة بمشروع واحد فقط. عندما تنشئ بيئة افتراضية لمشروعك، فإن أي مكتبة تقوم بتثبيتها ستكون موجودة فقط داخل هذه البيئة، ولن تؤثر على مشاريعك الأخرى.
إنها أفضل ممارسة يجب على كل مبرمج بايثون اتباعها.
كيفية إنشاء وتفعيل بيئة افتراضية
ملاحظة هامة: الأوامر التالية يتم تنفيذها في سطر الأوامر (Terminal أو Command Prompt) في نظام التشغيل الخاص بك، وليس داخل ملف أو محرر بايثون.
أولاً، افتح سطر الأوامر، اذهب إلى مجلد مشروعك، ثم نفذ الأمر التالي لإنشاء مجلد بيئة افتراضية باسم venv
.
python -m venv venv
ثانياً، يجب عليك “تفعيل” البيئة في كل مرة تريد العمل على مشروعك.
- على Windows:
venv\Scripts\activate
- على macOS و Linux:
source venv/bin/activate
عند تفعيل البيئة، ستلاحظ أن اسم البيئة (venv
) يظهر في بداية سطر الأوامر.
ثالثاً، عندما تنتهي من العمل، يمكنك الخروج من البيئة الافتراضية ببساطة عن طريق كتابة الأمر deactivate
.
deactivate
2. pip
: مدير حزم بايثون
الآن بعد أن أصبحت داخل بيئتك الافتراضية المفعلة، يمكنك استخدام pip
بأمان. pip
هي الأداة الرسمية التي تتصل بمستودع ضخم على الإنترنت يسمى Python Package Index (PyPI) لجلب وتثبيت المكتبات.
أ. تثبيت مكتبة
سنقوم الآن بتثبيت مكتبة requests
، والتي تستخدم لإجراء طلبات عبر الإنترنت.
pip install requests
ب. عرض المكتبات المثبتة
لمعرفة كل المكتبات الخارجية المثبتة في بيئتك الحالية، استخدم الأمر pip list
.
pip list
3. التحدث مع الإنترنت: واجهات برمجة التطبيقات (APIs)
ما هي واجهة برمجة التطبيقات (API)؟
تخيل أنك في مطعم. أنت (البرنامج العميل) لا تدخل إلى المطبخ (قاعدة بيانات الخادم) بنفسك لطهي طعامك. بدلاً من ذلك، أنت تستخدم قائمة الطعام (الـ API).
- أنت تنظر في القائمة لترى ما هو متاح.
- تطلب طلبًا محددًا من النادل (“أريد المنتج رقم 1”).
- النادل (الـ API) يذهب إلى المطبخ ويحضر لك طلبك بالضبط في طبق منظم (بيانات بصيغة JSON).
واجهة برمجة التطبيقات (API) هي وسيط منظم يسمح لبرنامجك بالتحدث مع برنامج آخر (مثل خادم ويب) وطلب البيانات منه بطريقة محددة وآمنة.
تشريح عنوان الـ API
غالبًا ما تكون عناوين الـ API منظمة بشكل منطقي.
https://dummyjson.com/products
: هذا يعيد قائمة بكل المنتجات.https://dummyjson.com/products/1
: هذا يعيد المنتج المحدد الذي له المعرف1
.
💡 هل تعلم؟ (أفعال HTTP الأخرى) نحن نستخدم
requests.get()
للحصول على (GET) البيانات. ولكن هناك “أفعال” أخرى شائعة في الـ API:
- POST: لإرسال بيانات جديدة (مثل إنشاء منتج جديد).
- PUT / PATCH: لتحديث بيانات موجودة.
- DELETE: لحذف بيانات.
استكشاف مكتبة requests
مع DummyJSON API
سنقوم الآن بكتابة سكربت لجلب بيانات منتج من واجهة برمجة تطبيقات عامة.
import requests
# 1. تحديد عنوان الـ API
api_url = "https://dummyjson.com/products/1"
try:
# 2. إرسال الطلب
response = requests.get(api_url)
# هذا السطر يثير استثناءً إذا كانت الاستجابة خطأ (مثل 404 أو 500)
response.raise_for_status()
# 3. تحويل بيانات JSON إلى قاموس بايثون
data = response.json()
# 4. استخراج وعرض المعلومات
product_title = data.get('title')
product_price = data.get('price')
if product_title and product_price:
print("تم جلب بيانات المنتج بنجاح:")
print(f"عنوان المنتج: {product_title}")
print(f"سعر المنتج: ${product_price}")
else:
print("لم يتم العثور على العنوان أو السعر في الاستجابة.")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ أثناء الاتصال بالـ API: {e}")
4. إدارة المشاريع باستخدام requirements.txt
عندما تود مشاركة مشروعك مع آخرين، كيف تضمن أنهم سيقومون بتثبيت نفس المكتبات التي استخدمتها بنفس الإصدارات؟ الحل هو ملف requirements.txt
.
لإنشاء هذا الملف، تأكد من أن بيئتك الافتراضية مفعلة، ثم نفذ الأمر التالي.
pip freeze > requirements.txt
عندما يحصل شخص آخر على مشروعك، كل ما عليه فعله لتثبيت كل المكتبات المطلوبة هو إنشاء بيئته الافتراضية، تفعيلها، ثم تشغيل أمر واحد:
pip install -r requirements.txt
5. تمرين تطبيقي: البحث عن منتجات حسب الفئة
المطلوب:
كتابة برنامج يتفاعل مع dummyjson.com
API للبحث عن منتجات تنتمي إلى فئة معينة يحددها المستخدم.
الحل المقترح:
الخطوة 1: إعداد بيئة المشروع من سطر الأوامر
mkdir product_searcher
cd product_searcher
python -m venv venv
# For Windows: venv\Scripts\activate
# For macOS/Linux: source venv/bin/activate
pip install requests
الخطوة 2: كتابة سكربت بايثون (ملف search_app.py
)
import requests
# عنوان الـ API لجلب كل المنتجات
api_url = "https://dummyjson.com/products"
# طلب الفئة من المستخدم وتحويلها إلى أحرف صغيرة
user_category = input("أدخل فئة المنتجات للبحث عنها (مثلاً: smartphones, laptops): ").lower()
all_products = [] # قائمة فارغة للبدء بها
try:
# إرسال الطلب وجلب كل البيانات
response = requests.get(api_url)
response.raise_for_status() # التحقق من أخطاء الطلب
data = response.json()
all_products = data.get('products', []) # الحصول على قائمة المنتجات بأمان
except requests.exceptions.RequestException as e:
print(f"لا يمكن الاتصال بالـ API: {e}")
# قائمة لتخزين المنتجات التي تطابق بحث المستخدم
matching_products = []
if all_products:
# المرور على كل منتج في القائمة
for product in all_products:
# التحقق إذا كانت فئة المنتج تطابق مدخلات المستخدم
if product.get('category', '').lower() == user_category:
# إذا تطابقت، أضف القاموس بأكمله إلى قائمتنا الجديدة
matching_products.append(product)
print(f"\n--- نتائج البحث في فئة: '{user_category}' ---")
# الآن، تحقق إذا كانت قائمتنا الجديدة تحتوي على أي شيء
if matching_products: # سيُقيّم إلى True إذا كانت القائمة غير فارغة
# إذا وجدنا منتجات، قم بطباعتها
for product in matching_products:
print(f"العنوان: {product['title']}, السعر: ${product['price']}")
else:
# إذا بقيت القائمة فارغة، فهذا يعني أننا لم نجد شيئًا
print("لم يتم العثور على منتجات في هذه الفئة.")
6. خلاصة الفصل
- البيئات الافتراضية هي ممارسة أساسية لعزل اعتماديات كل مشروع ومنع التضارب.
- نستخدم وحدة
venv
لإنشاء البيئات، وactivate
لتفعيلها. pip
هي أداتك الأساسية لتثبيت وإدارة المكتبات الخارجية داخل البيئة المفعلة.- ملف
requirements.txt
ضروري لتوثيق ومشاركة اعتماديات مشروعك.
تمارين تطبيقية مع الحلول
التمرين 1: جلب منشور واحد
المطلوب:
باستخدام مكتبة requests
، اكتب برنامجًا يقوم بجلب بيانات منشور واحد من واجهة برمجة التطبيقات DummyJSON باستخدام الرابط: https://dummyjson.com/posts/5
. بعد جلب البيانات، قم بطباعة عنوان المنشور (title
) ومحتواه (body
).
الحل:
# --- الحل للتمرين 1 ---
import requests
# تحديد عنوان الـ API للمنشور رقم 5
api_url = "https://dummyjson.com/posts/5"
try:
# إرسال طلب GET
response = requests.get(api_url)
response.raise_for_status() # للتحقق من وجود أخطاء مثل 404
# تحويل الاستجابة إلى قاموس بايثون
data = response.json()
# استخراج البيانات المطلوبة وطباعتها
post_title = data.get('title')
post_body = data.get('body')
print(f"عنوان المنشور: {post_title}")
print("--- المحتوى ---")
print(post_body)
except requests.exceptions.RequestException as e:
print(f"حدث خطأ في الطلب: {e}")
التمرين 2: التعامل مع خطأ 404
المطلوب:
اكتب برنامجًا يحاول جلب بيانات منشور غير موجود (مثلاً، منشور رقم 500
) من الرابط: https://dummyjson.com/posts/500
. استخدم كتلة try...except
مع response.raise_for_status()
لمعالجة خطأ HTTP (مثل 404 Not Found) وطباعة رسالة واضحة للمستخدم تفيد بأن “المنشور غير موجود”.
الحل:
# --- الحل للتمرين 2 ---
import requests
# عنوان API لمنشور غير موجود
api_url = "https://dummyjson.com/posts/500"
print(f"محاولة جلب المنشور من: {api_url}")
try:
response = requests.get(api_url)
# هذا السطر سيثير استثناءً لأن حالة الاستجابة ستكون 404
response.raise_for_status()
data = response.json()
print("تم جلب البيانات بنجاح!") # هذا السطر لن يتم الوصول إليه
except requests.exceptions.HTTPError as e:
# نلتقط خطأ HTTP تحديدًا
# يمكننا التحقق من رمز الحالة إذا أردنا
if e.response.status_code == 404:
print("خطأ: هذا المنشور غير موجود (خطأ 404).")
else:
print(f"حدث خطأ HTTP: {e}")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ في الاتصال: {e}")
التمرين 3: عرض قائمة المهام (Todos)
المطلوب:
استخدم الرابط https://dummyjson.com/todos
لجلب قائمة بكل المهام. اكتب برنامجًا يقوم بالمرور على هذه القائمة ويطبع نص المهمة (todo
) لكل مهمة.
الحل:
# --- الحل للتمرين 3 ---
import requests
api_url = "https://dummyjson.com/todos"
try:
response = requests.get(api_url)
response.raise_for_status()
data = response.json()
# البيانات التي تهمنا موجودة داخل المفتاح 'todos'
all_todos = data.get('todos', [])
print("--- قائمة كل المهام ---")
for todo_item in all_todos:
print(f"- {todo_item.get('todo')}")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ: {e}")
التمرين 4: فلترة المهام المكتملة
المطلوب:
بناءً على التمرين السابق، قم بتعديل البرنامج ليطبع فقط المهام التي تم إكمالها ("completed": true
). في النهاية، اطبع العدد الإجمالي للمهام المكتملة.
الحل:
# --- الحل للتمرين 4 ---
import requests
api_url = "https://dummyjson.com/todos"
try:
response = requests.get(api_url)
response.raise_for_status()
data = response.json()
all_todos = data.get('todos', [])
completed_count = 0
print("--- المهام المكتملة فقط ---")
for todo_item in all_todos:
# التحقق من قيمة المفتاح 'completed'
if todo_item.get('completed') == True:
print(f"- {todo_item.get('todo')}")
completed_count += 1
print("-" * 20)
print(f"العدد الإجمالي للمهام المكتملة: {completed_count}")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ: {e}")
التمرين 5: استخدام البحث في الـ API
المطلوب:
توفر DummyJSON نقطة نهاية للبحث عن المنتجات: https://dummyjson.com/products/search?q=QUERY
. اكتب برنامجًا يسأل المستخدم عن مصطلح بحث (مثل “phone” أو “watch”)، ثم يقوم البرنامج ببناء الرابط، وإرسال الطلب، وطباعة أسماء كل المنتجات التي تم العثور عليها.
الحل:
# --- الحل للتمرين 5 ---
import requests
# 1. طلب مصطلح البحث من المستخدم
search_query = input("أدخل اسم المنتج الذي تبحث عنه: ")
# 2. بناء رابط الـ API باستخدام f-string
api_url = f"https://dummyjson.com/products/search?q={search_query}"
print(f"يتم الآن البحث في: {api_url}")
try:
response = requests.get(api_url)
response.raise_for_status()
data = response.json()
products = data.get('products', [])
print(f"\n--- نتائج البحث عن '{search_query}' ---")
if products:
for product in products:
print(f"- {product.get('title')}")
else:
print("لم يتم العثور على منتجات مطابقة.")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ: {e}")
التمرين 6: إنشاء ملف requirements.txt
المطلوب: هذا تمرين إجرائي يتم تنفيذه في سطر الأوامر (Terminal). قم بتنفيذ الخطوات التالية:
- أنشئ مجلدًا جديدًا لمشروعك وانتقل إليه.
- أنشئ بيئة افتراضية باسم
my_env
. - فعّل البيئة.
- استخدم
pip
لتثبيت مكتبةrequests
. - قم بإنشاء ملف
requirements.txt
يحتوي على المكتبات المثبتة.
الحل:
# هذه الأوامر تُنفذ في سطر الأوامر
# الخطوة 1
mkdir new_project
cd new_project
# الخطوة 2
python -m venv my_env
# الخطوة 3 (على macOS/Linux)
# source my_env/bin/activate
# على Windows: my_env\Scripts\activate
# الخطوة 4
pip install requests
# الخطوة 5
pip freeze > requirements.txt
# الآن، إذا فتحت ملف requirements.txt، ستجد بداخله مكتبة requests وكل اعتمادياتها
التمرين 7: جلب بيانات المستخدمين
المطلوب:
استخدم الرابط https://dummyjson.com/users
لجلب قائمة من المستخدمين. قم بالمرور على القائمة وطباعة الاسم الكامل (firstName
+ lastName
) والبريد الإلكتروني لكل مستخدم.
الحل:
# --- الحل للتمرين 7 ---
import requests
api_url = "https://dummyjson.com/users"
try:
response = requests.get(api_url)
response.raise_for_status()
data = response.json()
users = data.get('users', [])
print("--- قائمة المستخدمين والبريد الإلكتروني ---")
for user in users:
full_name = f"{user.get('firstName')} {user.get('lastName')}"
email = user.get('email')
print(f"الاسم: {full_name}, البريد الإلكتروني: {email}")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ: {e}")
التمرين 8: جلب بيانات من قاموس متداخل
المطلوب:
قم بجلب بيانات المستخدم رقم 2
من الرابط: https://dummyjson.com/users/2
. البيانات المستلمة تحتوي على قاموس متداخل للموقع (address
). قم بطباعة اسم المستخدم الأول، والمدينة (city
) التي يسكن فيها.
الحل:
# --- الحل للتمرين 8 ---
import requests
api_url = "https://dummyjson.com/users/2"
try:
response = requests.get(api_url)
response.raise_for_status()
user_data = response.json()
# الوصول للاسم الأول
first_name = user_data.get('firstName')
# الوصول للقاموس المتداخل 'address'
address_info = user_data.get('address', {})
# الوصول للمدينة من داخل قاموس العنوان
city = address_info.get('city')
print(f"اسم المستخدم: {first_name}")
print(f"المدينة: {city}")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ: {e}")
التمرين 9: حساب متوسط الأسعار
المطلوب:
قم بجلب كل المنتجات من https://dummyjson.com/products
. قم بالمرور على كل المنتجات واجمع أسعارها، ثم احسب متوسط سعر المنتج الواحد.
الحل:
# --- الحل للتمرين 9 ---
import requests
api_url = "https://dummyjson.com/products"
try:
response = requests.get(api_url)
response.raise_for_status()
data = response.json()
products = data.get('products', [])
if products:
total_price = 0
# المرور على كل المنتجات لجمع الأسعار
for product in products:
total_price += product.get('price', 0)
# حساب المتوسط
average_price = total_price / len(products)
print(f"العدد الإجمالي للمنتجات: {len(products)}")
print(f"متوسط سعر المنتج الواحد هو: ${average_price:.2f}")
else:
print("لم يتم العثور على منتجات.")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ: {e}")
التمرين 10: جلب كل التعليقات لمنشور معين
المطلوب:
توفر DummyJSON نقطة نهاية لجلب كل التعليقات الخاصة بمنشور معين، على سبيل المثال: https://dummyjson.com/posts/1/comments
.
اكتب برنامجًا يطلب من المستخدم إدخال رقم منشور، ثم يقوم بجلب كل التعليقات الخاصة به ويطبع محتوى كل تعليق (body
).
الحل:
# --- الحل للتمرين 10 ---
import requests
# 1. طلب رقم المنشور من المستخدم
post_id = input("أدخل رقم المنشور لجلب تعليقاته (مثلاً: 1): ")
# 2. بناء الرابط ديناميكيًا
api_url = f"https://dummyjson.com/posts/{post_id}/comments"
try:
response = requests.get(api_url)
response.raise_for_status()
data = response.json()
comments = data.get('comments', [])
print(f"\n--- تعليقات المنشور رقم {post_id} ---")
if comments:
for comment in comments:
print(f"- {comment.get('body')}")
else:
print("لا توجد تعليقات لهذا المنشور.")
except requests.exceptions.RequestException as e:
print(f"حدث خطأ، قد يكون رقم المنشور غير صحيح: {e}")
ماذا بعد؟
الآن بعد أن أصبحت قادرًا على كتابة كود منظم واستخدام أدوات خارجية قوية ضمن بيئات عمل احترافية، حان الوقت لنتعلم في الفصل الثالث عشر كيف ننظم ملفات مشروعنا نفسه بطريقة أنظف باستخدام الوحدات (Modules) والحزم (Packages).