أهلاً بك مجدداً في رحلتك لبناء وكلاء الذكاء الاصطناعي!
في درسنا السابق، قمنا بالخطوة الأولى والمثيرة للتواصل مع الإنترنت عبر كود بايثون. تعلمنا كيف نرسل طلبات GET لجلب البيانات وPOST لإنشاء بيانات جديدة باستخدام مكتبة requests. ورأينا كيف أن الدالة response.json() تحول الرد القادم من الخادم إلى قاموس بايثون يمكننا التعامل معه.
اليوم، سنتعمق أكثر في هذه الخطوة الأخيرة. ففي كثير من الأحيان، لا تكون البيانات التي نتلقاها مجرد قاموس بسيط. قد تكون قائمة طويلة من العناصر، أو قاموساً يحتوي على قواميس أخرى بداخله. هدفنا اليوم هو أن نتعلم كيف "نشرح" هذه الاستجابات المعقدة ونستخرج منها المعلومات التي نحتاجها بدقة. هذه المهارة ضرورية جداً، لأن وكيل الذكاء الاصطناعي يعتمد على قدرته على فهم البيانات التي يحصل عليها من أدواته (مثل واجهات برمجة التطبيقات) لاتخاذ قرارات ذكية.
لنتخيل أننا في الدرس السابق تعلمنا كيف نطلب قائمة الطعام (Request) وكيف نستلمها (Response). اليوم، سنتعلم كيف نقرأ قائمة الطعام هذه بالتفصيل، ونختار منها الطبق الذي نريده بالضبط.

1. ما هو تنسيق JSON؟ لغة البيانات المشتركة
عندما تتواصل البرامج المختلفة عبر الإنترنت، فإنها تحتاج إلى لغة مشتركة لتبادل البيانات. التنسيق الأكثر شيوعاً واستخداماً اليوم هو JSON (تُنطق "جيسون")، وهي اختصار لـ JavaScript Object Notation.
ببساطة، JSON هو تنسيق نصي لتمثيل البيانات بطريقة منظمة وسهلة القراءة للبشر والآلات على حد سواء.
العمل مع JSON في بايثون: دليل سهل للمبتدئين
لفهم أساسيات JSON، ألقِ نظرة على المقال التالي من Apidog. سيركز شرحي على النقاط الأساسية التي تحتاجها.
اقرأ فقط القسم الأول بعنوان 'ما هو JSON؟'. ركز على شكل الكود وكيف يشبه قاموس بايثون الذي تعرفه.
كما رأيت في المقال، يتكون JSON من لبنتين أساسيتين:
- الأزواج (Key-Value Pairs): تماماً مثل قواميس بايثون، يتكون JSON من مفاتيح وقيم (
"key": value). المفتاح يكون دائماً نصاً بين علامتي اقتباس مزدوجتين. - المصفوفات (Arrays): وهي تماثل القوائم (Lists) في بايثون، وتُكتب بين قوسين مربعين
[].
عندما تستلم بايثون بيانات JSON، فإنها تحولها إلى أنواع البيانات التي تفهمها:
- كائن JSON
{...}⬅️ قاموس بايثونdict - مصفوفة JSON
[...]⬅️ قائمة بايثونlist - نص
"..."⬅️ سلسلة نصيةstr - رقم
123أو12.3⬅️ عدد صحيحintأو عشريfloat - قيمة منطقية
trueأوfalse⬅️TrueأوFalse - القيمة الفارغة
null⬅️None

2. كيف تحول بايثون النص إلى بيانات؟
في الدرس السابق، استخدمنا response.json() بسلاسة. لكن ما الذي يحدث خلف الكواليس؟ ما تفعله هذه الدالة هو أخذ النص الخام من استجابة الـ API، والذي يكون بتنسيق JSON، وتحويله (أو "تحليله" - Parsing) إلى كائن بايثون (قاموس أو قائمة).
توفر بايثون وحدة مدمجة تسمى json لهذا الغرض. الدالة الأساسية فيها هي json.loads() (لاحظ حرف s في النها_ية، وهي اختصار لكلمة "string").
لنرى مثالاً بسيطاً:
import json
# لنفترض أن هذا هو النص الذي استلمناه من API
json_string = '{"name": "Ahmed", "is_student": true, "courses": ["Math", "Physics"]}'
# استخدم json.loads() لتحويل النص إلى قاموس بايثون
python_dict = json.loads(json_string)
# الآن python_dict هو قاموس عادي يمكننا التعامل معه
print(python_dict)
print(f"الاسم: {python_dict['name']}")
print(f"عدد المواد: {len(python_dict['courses'])}")
عند تشغيل هذا الكود، ستكون المخرجات:
{'name': 'Ahmed', 'is_student': True, 'courses': ['Math', 'Physics']}
الاسم: Ahmed
عدد المواد: 2
إذن، response.json() هي مجرد طريقة مختصرة ومريحة توفرها مكتبة requests لتنفيذ هذه العملية مباشرة على كائن الاستجابة.
لفهم أعمق، شاهد هذا المقطع القصير الذي يوضح الفرق بين json.loads() (للتعامل مع السلاسل النصية) وjson.load() (للتعامل مع الملفات، والتي سنتطرق لها لاحقاً).
Python Tutorial: Working with JSON Data using the json Module
يشرح هذا الفيديو من Corey Schafer بالتفصيل كيفية استخدام وحدة json في بايثون. على الرغم من أن الفيديو باللغة الإنجليزية، إلا أن الأمثلة البرمجية واضحة جداً وتوضح المفاهيم التي نناقشها.
شاهد من بداية الفيديو حتى الدقيقة 04:27. ركز على: شكل بيانات JSON الأولية كسلسلة نصية (string). كيفية استخدام json.loads() لتحويلها إلى قاموس بايثون. جدول التحويل بين أنواع بيانات JSON وأنواع بيانات بايثون (حوالي الدقيقة 02:00). كيف بدأ بالوصول إلى البيانات بعد تحويلها (مثل الدخول في حلقة تكرارية).
3. التطبيق العملي: تشريح استجابة API
الآن بعد أن فهمنا الأساسيات، لنعد إلى واجهة JSONPlaceholder ونحلل بعض الاستجابات الأكثر واقعية.
السيناريو الأول: التعامل مع قائمة من العناصر
في الدرس السابق، جلبنا منشوراً واحداً (/posts/1). ماذا لو أردنا جلب قائمة بأول 5 منشورات؟ الرابط سيكون https://jsonplaceholder.typicode.com/posts.
import requests
# سنضيف `?_limit=5` في نهاية الرابط ليُرجع لنا 5 عناصر فقط لتسهيل العرض
url = "https://jsonplaceholder.typicode.com/posts?_limit=5"
response = requests.get(url)
# التحقق من نجاح الطلب
if response.status_code == 200:
# response.json() سيعيد لنا الآن قائمة (list) من القواميس
posts_list = response.json()
print(f"نوع البيانات المستلمة: {type(posts_list)}")
print(f"عدد المنشورات المستلمة: {len(posts_list)}")
print("---")
# يمكننا الآن استخدام حلقة تكرارية للمرور على كل منشور في القائمة
for post in posts_list:
# كل 'post' هو قاموس منفصل
post_id = post['id']
post_title = post['title']
print(f"عنوان المنشور رقم {post_id}: {post_title}")
else:
print(f"فشل الطلب، رمز الحالة: {response.status_code}")
هذا النمط شائع جداً: تتلقى قائمة، تمر عبرها بحلقة for، ثم تتعامل مع كل عنصر (وهو قاموس) على حدة.
السيناريو الثاني: التعامل مع بيانات متداخلة (Nested Data)
أحياناً، يحتوي القاموس على قواميس أخرى بداخله. لنجلب بيانات مستخدم واحد من الرابط https://jsonplaceholder.typicode.com/users/1.
import requests
url = "https://jsonplaceholder.typicode.com/users/1"
response = requests.get(url)
if response.status_code == 200:
user_data = response.json()
# الوصول إلى قيمة على المستوى الأول
user_name = user_data['name']
user_email = user_data['email']
print(f"اسم المستخدم: {user_name} ({user_email})")
# الوصول إلى قيمة داخل قاموس متداخل
# user_data['address'] يعطينا القاموس الخاص بالعنوان
# ثم نستخدم ['city'] للوصول إلى قيمة المدينة داخل ذلك القاموس
user_city = user_data['address']['city']
print(f"المدينة: {user_city}")
# يمكننا حتى الوصول إلى بيانات أعمق!
geo_lat = user_data['address']['geo']['lat']
print(f"خط العرض الجغرافي: {geo_lat}")
else:
print(f"فشل الطلب، رمز الحالة: {response.status_code}")
لاحظ كيف نستخدم الأقواس المربعة [] بشكل متسلسل للغوص أعمق في بنية البيانات. هذا يشبه تماماً التنقل بين المجلدات في حاسوبك.
اختبر فهمك!
توفر واجهة JSONPlaceholder نقطة نهاية للتعليقات (comments). جرب إرسال طلب GET إلى الرابط https://jsonplaceholder.typicode.com/comments/1.
الاستجابة التي ستحصل عليها ستكون قاموساً يحتوي على مفاتيح مثل postId, id, name, email, و body.
اكتب برنامجاً صغيراً يقوم بما يلي:
- يجلب بيانات التعليق رقم 1.
- يطبع جملة بالصيغة التالية:
التعليق من [email] يقول: "[body]"، مستخدماً القيم من البيانات التي تم جلبها.
أظهر الإجابة
import requests
url = "https://jsonplaceholder.typicode.com/comments/1"
response = requests.get(url)
if response.status_code == 200:
comment_data = response.json()
# استخراج البريد الإلكتروني ونص التعليق
email = comment_data['email']
body = comment_data['body']
# طباعة الجملة المطلوبة
print(f"التعليق من {email} يقول: \"{body}\"")
else:
print(f"فشل الطلب، رمز الحالة: {response.status_code}")
ملخص الدرس وأهم النقاط
أحسنت! لقد اكتسبت اليوم مهارة لا غنى عنها لأي مبرمج يتعامل مع الإنترنت. لم نعد نكتفي باستلام البيانات، بل أصبحنا قادرين على فهمها واستخلاص ما يهمنا منها.
أهم النقاط التي استخلصناها:
- JSON هو التنسيق القياسي لتبادل البيانات في واجهات برمجة التطبيقات (APIs).
- يشبه JSON قواميس وقوائم بايثون، مما يسهل التعامل معه.
- الدالة
response.json()تقوم بتحليل نص JSON وتحويله إلى كائن بايثون (عادةًdictأوlist). - يمكننا التنقل داخل البيانات المعقدة باستخدام:
- حلقات
forللمرور على القوائم. - الوصول عبر المفتاح
['key']للوصول إلى القيم داخل القواميس، حتى لو كانت متداخلة.
- حلقات
ما التالي؟
حتى الآن، كل واجهات برمجة التطبيقات التي تعاملنا معها كانت عامة ومفتوحة للجميع. لكن معظم الـ APIs القوية والمفيدة (مثل واجهات OpenAI، Google Maps, Twitter) تتطلب منك التعريف بنفسك قبل أن تسمح لك بالوصول إلى بياناتها. يتم ذلك عادةً باستخدام ما يسمى "مفتاح API" (API Key).
في الدرس القادم، سنتعلم كيف نُدرج مفتاح الـ API في طلباتنا للوصول إلى خدمات محمية، وهي خطوة حاسمة ستقربنا أكثر من التفاعل مع نماذج اللغة الكبيرة وبناء وكيلنا الذكي.