Menu
العربية

طلبات HTTP في بايثون باستخدام مكتبة requests

تعلّم كيف ترسل طلبات HTTP في بايثون باستخدام مكتبة requests: GET وPOST، ومعاملات الاستعلام، والترويسات، وإرسال JSON، والتعامل مع الأخطاء.

جلب البيانات من الإنترنت ببضعة أسطر فقط

أغلب برامج بايثون الحقيقية تحتاج في مرحلة ما للتواصل مع شيء ما عبر الشبكة — واجهة REST API، خدمة طقس، نقطة وصول على GitHub، أو حتى تنزيل ملف. صحيح أن المكتبة القياسية تقدّر تسوّي هذا عبر urllib، لكن الأداة الأكثر شيوعًا في عالم بايثون لإرسال طلبات HTTP في بايثون هي مكتبة خارجية اسمها requests. واجهتها أبسط وأوضح بكثير، وتستاهل فعلًا أمر pip install واحد.

pip install requests

إذا لم تكن تعمل بالفعل داخل بيئة افتراضية، جهّز واحدة أولًا — فهذا يجعل التثبيت محصورًا في مشروعك دون أن يؤثر على بقية النظام.

أول طلب GET في بايثون

main.py
Output
Click Run to see the output here.

ثلاثة أسطر فقط: نستدعي get، ثم نفحص رمز الحالة، ثم نلقي نظرة على جسم الاستجابة. الخاصية response.text تعطيك جسم الاستجابة كسلسلة نصية. اختصرناها هنا لأن الـ JSON الكامل طويل.

رموز الحالة (Status Codes) التي يستحسن أن تحفظها كمرجع سريع:

  • 200 — تمام، كل شيء اشتغل كما يجب.
  • 201 — تم الإنشاء (استجابة شائعة لطلب POST).
  • 301 / 302 — إعادة توجيه؛ ومكتبة requests تتبعها تلقائيًا بشكل افتراضي.
  • 400 — طلب خاطئ؛ هناك شيء غير صحيح فيما أرسلته.
  • 401 / 403 — غير مُصادَق عليه / غير مُخوَّل بالوصول.
  • 404 — المورد المطلوب غير موجود.
  • 429 — تجاوزت حد الطلبات المسموح؛ خفّف من وتيرة الطلبات.
  • 500 — خطأ من جهة الخادم.

تحليل استجابة JSON في بايثون

عندما يُرجع الـ endpoint بيانات بصيغة JSON، استخدم .json() على كائن الاستجابة — فهذه الدالة تحلّل الجسم وتعيده لك على شكل قاموس (dict) أو قائمة (list):

main.py
Output
Click Run to see the output here.

خلف الكواليس، .json() يكافئ تمامًا json.loads(response.text) — مجرد اختصار مريح للحالة الشائعة.

تمرير مُعاملات الاستعلام (Query Parameters)

لا تلجأ إلى لصق ?key=value&... يدويًا في نهاية الرابط. بدلًا من ذلك، مرّر قاموسًا (dict) عبر الوسيط params=:

main.py
Output
Click Run to see the output here.

requests تتكفّل بترميز الـ URL نيابةً عنك — المسافات والرموز الخاصة وأحرف اليونيكود، كلّها تمرّ بأمان.

الـ URL الفعلي الذي أُرسل موجود في response.url، وهذا مفيد جداً وقت تصحيح الأخطاء.

إرسال طلب POST بجسم JSON

لإرسال البيانات — سواء لإنشاء موارد جديدة، أو تقديم نماذج، أو استدعاء أي endpoint يُحدث تغييراً — استخدم requests.post:

import requests

payload = {
    "title": "Docs update",
    "body": "Added HTTP requests page.",
    "labels": ["docs"],
}

response = requests.post(
    "https://api.example.com/issues",
    json=payload,
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)

print(response.status_code)
print(response.json())

المعامل json= يقوم بأمرين في آنٍ واحد: يحوّل payload إلى صيغة JSON، ويضبط ترويسة Content-Type: application/json تلقائياً. يمكنك طبعاً فعل ذلك يدوياً عبر data=json.dumps(payload) مع تمرير الترويسة بشكل صريح، لكن json= هو الاختصار المتعارف عليه في مكتبة requests.

أمّا إذا كنت تريد إرسال بيانات على هيئة نموذج (أي بصيغة form-encoded كما ترسلها نماذج HTML التقليدية)، فاستخدم data= بدلاً من ذلك:

requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})

الترويسات (Headers)

يمكنك تمرير أي ترويسات مخصصة على هيئة قاموس (dict):

import requests

response = requests.get(
    "https://api.example.com/profile",
    headers={
        "Authorization": "Bearer abc123",
        "User-Agent": "my-tool/1.0",
    },
)

معظم الـ APIs تتطلب ترويسة Authorization للمصادقة. نوع المصادقة بالضبط (Bearer أو Basic أو Token) ستجده موثقاً في وثائق الـ API نفسه.

لماذا timeout ليس خياراً رفاهياً

افتراضياً، مكتبة requests ستنتظر الرد إلى ما لا نهاية. وفي أي برنامج حقيقي، هذا يعني أن خادماً متذبذباً كفيل بتعليق سكربتك بالكامل. لذلك مرّر دائماً قيمة timeout:

import requests

try:
    response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
    print("Server took too long.")

الرقم هنا بالثواني. timeout=5 يعني "إذا ما وصل الرد خلال 5 ثوانٍ، اعتبرها فاشلة." وتقدر تمرّر tuple على الشكل (connect_timeout, read_timeout) إذا كنت تحتاج تحكّم أدق.

التعامل مع الأخطاء في requests

في الغالب، تواجه نوعين من المشاكل:

  1. أخطاء على مستوى HTTP (4xx و 5xx) — يعني الخادم ردّ عليك فعلاً، لكن الرد يحمل خطأ. تعرف ذلك عن طريق response.status_code.
  2. مشاكل على مستوى الشبكة — مثل انتهاء المهلة (timeout)، فشل DNS، أو تعذّر الوصول للخادم. هذي المشاكل ترفع استثناءات (exceptions).

الأسلوب المتعارف عليه بين مطوّري بايثون يجمع بين الحالتين معاً:

main.py
Output
Click Run to see the output here.

الدالة raise_for_status() لا تفعل شيئًا مع استجابات 2xx، لكنها ترفع استثناءً في الحالات الأخرى. أما RequestException فهو الكلاس الأساسي لكل الأخطاء التي ترميها مكتبة requests، فيكفيك except واحد لالتقاط "أي شيء فشل أثناء التواصل مع هذا الـ endpoint".

تنزيل ملف باستخدام requests

لو كنت بصدد تنزيل ملف ثنائي كبير، استخدم التدفق (streaming) حتى لا يتم تحميل الملف كاملًا في الذاكرة:

import requests

url = "https://example.com/large.zip"

with requests.get(url, stream=True, timeout=30) as r:
    r.raise_for_status()
    with open("large.zip", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

stream=True تخبر requests بعدم تحميل جسم الاستجابة بالكامل دفعة واحدة، بينما iter_content(chunk_size=...) تسلّم لك المحتوى على شكل قطع صغيرة تكتبها مباشرة إلى القرص.

الجلسات: إعادة استخدام الاتصال وتوحيد الإعدادات الافتراضية

إذا كنت تنوي إرسال عدة طلبات HTTP إلى نفس الخدمة، فاستخدم Session. هذا الأسلوب يعيد استخدام اتصال TCP نفسه (وهو أسرع)، كما يتيح لك ضبط الإعدادات الافتراضية مرة واحدة فقط:

import requests

session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})

# Every request through this session carries the header.
a = session.get("https://api.example.com/users/1")
b = session.get("https://api.example.com/users/2")
c = session.post("https://api.example.com/users", json={"name": "Rosa"})

لو عندك سكربت بيضرب نفس الـ API عشرات المرات، استخدام الـ session هيفرق معاك في السرعة بشكل ملحوظ.

مثال واقعي: عميل بسيط لـ GitHub

نجيب آخر إصدار (release) لأي ريبو:

import requests

def latest_release(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()
    return {
        "tag": data["tag_name"],
        "name": data["name"],
        "published": data["published_at"],
        "url": data["html_url"],
    }

release = latest_release("python", "cpython")
print(release)

أقل من خمسة عشر سطرًا: تجهّز الرابط، ترسل الطلب، تتحقق من الأخطاء، ثم تستخرج الحقول التي تهمّك. هذا هو الشكل العام لأغلب عملاء الـ API اللي هتكتبهم.

ماذا عن urllib؟

مكتبة urllib.request الموجودة ضمن المكتبة القياسية تقدر تعمل كل ما تعمله مكتبة requests — لكن بأسطر أكثر وتجربة استخدام أسوأ. لو كنت مضطرًا فعلًا لعدم إضافة أي اعتمادية خارجية، فهي متاحة:

import json
import urllib.request

with urllib.request.urlopen("https://api.github.com/repos/python/cpython") as r:
    data = json.loads(r.read().decode("utf-8"))

print(data["name"])

لأي شيء يتجاوز سكريبت سريع، فإن مكتبة requests (أو httpx إذا كنت بحاجة إلى دعم غير متزامن) تستحق التثبيت فعلاً.

عادات مفيدة عند استخدام requests

  • حدِّد timeout دائماً. بدون استثناءات.
  • استخدم raise_for_status() في السكريبتات التي تتوقع نجاح الطلب — فهي تحوّل أي استجابة فاشلة إلى استثناء واضح وصاخب.
  • مرِّر القواميس إلى params= و json= بدلاً من بناء السلاسل النصية يدوياً.
  • اجمع الطلبات المترابطة داخل Session عندما تتعامل مع نفس الـ API عدة مرات.
  • سجِّل response.status_code و response.text أثناء تصحيح الأخطاء — فالمحتوى غالباً ما يخبرك تماماً بما لم يعجب الخادم.

الخطوة التالية: التواريخ والأوقات

بوجود requests ضمن أدواتك، أصبح بإمكانك التحدث مع أي Web API حديث، وتنزيل الملفات، وبناء تكاملات صغيرة بين الخدمات المختلفة. وبدمج ما تعلمته هنا مع صفحتَي JSON و CSV السابقتين، تكون قد أتقنت الدورة الكاملة: "اجلب البيانات، اقرأها، عالجها، ثم أعِد كتابتها" — وهي البنية التي تقوم عليها أعداد هائلة من سكريبتات بايثون الواقعية. لكن معظم هذه البيانات يحمل طابعاً زمنياً، والصفحة التالية تشرح كيف تمثّل بايثون التواريخ والأوقات، وما هي فخاخ المناطق الزمنية التي يجدر بك تجنّبها.

الأسئلة الشائعة

كيف أرسل طلب HTTP في بايثون؟

ثبّت المكتبة أولاً بالأمر pip install requests، ثم استخدم requests.get(url) لطلب GET أو requests.post(url, json=...) لطلب POST. كائن الاستجابة يوفّر لك .status_code و.text و.json() و.headers. مثال بسيط: r = requests.get('https://api.example.com/users/1').

أيهما أفضل: requests أم urllib؟

في الغالب ستجد أن requests أسهل بكثير وأقل كتابة، خصوصاً عند التعامل مع JSON والجلسات (sessions). أما urllib فهي مدمجة مع بايثون ولا تحتاج تثبيت، وهي مناسبة حين تكون إضافة أي اعتمادية ممنوعة. وفي بيئات الإنتاج، كثير من الفرق صارت تستخدم httpx لأنها متوافقة مع واجهة requests وتدعم async.

كيف أرسل JSON في طلب POST باستخدام بايثون؟

مرّر القاموس مباشرة عبر المعامل json، مثل: requests.post(url, json={'key': 'value'}). المكتبة ستتكفّل بتحويل القاموس إلى JSON وضبط الترويسة Content-Type: application/json نيابةً عنك. انتبه: لا تجمع بين data= وjson= في نفس الطلب، استخدم أحدهما فقط.

كيف أتعامل مع الأخطاء في مكتبة requests؟

إما أن تفحص response.status_code يدوياً (القيمة 200 تعني نجاح الطلب)، أو تستدعي response.raise_for_status() ليُطلق استثناءً تلقائياً عند أي استجابة 4xx أو 5xx. أما مشاكل الشبكة نفسها مثل انتهاء المهلة أو فشل DNS فترفع استثناءات مشتقّة من requests.RequestException، لذلك اصطياد هذا الصنف يغطّي الحالتين معاً.

تعلّم البرمجة مع Coddy

ابدأ الآن