تُعد مكتبة Requests الخيار الأمثل لإجراء طلبات HTTP في بايثون. فهي تُبسط عملية إرسال الطلبات من خلال واجهة برمجة تطبيقات سهلة الاستخدام. ورغم أنها ليست جزءًا من مكتبة بايثون القياسية، إلا أنه يُنصح باستخدام Requests لتنفيذ عمليات HTTP مثل GET وPOST وغيرها.
يرشدك هذا الدرس خلال عملية تخصيص الطلبات باستخدام الرؤوس والبيانات، ومعالجة الاستجابات، والمصادقة، وتحسين الأداء باستخدام الجلسات وإعادة المحاولات.
البدأ باستخدام مكتبة Requests في بايثون
على الرغم من أن مكتبة Requests تُعدّ من الأدوات الأساسية الشائعة لدى العديد من مطوري بايثون، إلا أنها غير مُدرجة في مكتبة بايثون القياسية. وبهذه الطريقة، يمكن للمكتبة أن تستمر في التطور بحرية أكبر كمشروع مستقل.
ملاحظة: إذا كنت ترغب في إجراء طلبات HTTP باستخدام مكتبة بايثون القياسية فقط، فإن urllib.request في بايثون هو خيار جيد لك.
بما أن مكتبة Requests هي مكتبة خارجية، فيجب تثبيتها قبل استخدامها في التعليمات البرمجية. ومن الممارسات الجيدة تثبيت الحزم الخارجية في بيئة افتراضية، ولكن يمكنك تثبيت Requests في بيئتك العامة إذا كنت تخطط لاستخدامها في مشاريع متعددة.
سواء كنت تعمل في بيئة افتراضية أم لا، ستحتاج إلى تثبيت requests:
$ python -m pip install requests
بمجرد أن ينتهي pip من تثبيت مكتبة requests، يمكنك استخدامها في تطبيقك. يبدو استيراد requests كالتالي:
import requests
الآن وقد انتهيت من الإعداد، حان الوقت لبدء رحلتك مع Requests. هدفك الأول هو إرسال طلب GET.
إرسال طلب GET
تُحدد طرق HTTP، مثل GET و POST، الإجراء المطلوب تنفيذه عند إرسال طلب HTTP. بالإضافة إلى GET و POST، توجد عدة طرق شائعة أخرى ستستخدمها لاحقًا في هذا الدرس.
تُعدّ طريقة GET من أكثر طرق HTTP استخدامًا، وهي تُستخدم لاسترجاع البيانات من مصدر مُحدد. لإجراء طلب GET باستخدام مكتبة Requests، يمكنك استدعاء الدالة ()requests.get.
لتجربة ذلك، يمكنك إرسال طلب GET إلى واجهة برمجة تطبيقات REST الخاصة بـ GitHub عن طريق استدعاء الدالة ()get باستخدام عنوان URL التالي:
>>> import requests
>>> requests.get("https://api.github.com")
<Response [200]>
تهانينا! لقد قدمت طلبك الأول. الآن ستتعمق قليلاً في الرد على هذا الطلب.
فحص الرد
الاستجابة هي الكائن الذي يحتوي على نتائج طلبك. حاول إجراء نفس الطلب مرة أخرى، ولكن هذه المرة قم بتخزين القيمة المُعادة في متغير حتى تتمكن من إلقاء نظرة فاحصة على خصائصها وسلوكياتها:
>>> import requests
>>> response = requests.get("https://api.github.com")
في هذا المثال، قمتَ باستلام القيمة المُعادة من دالة ()requests.get. وهي عبارة عن نسخة من كائن Response، وقمتَ بتخزينها في متغير يُسمى response. يمكنك الآن استخدام response لعرض معلومات كثيرة حول نتائج طلب GET الخاص بك.
العمل مع رموز الحالة
أول معلومة يمكنك الحصول عليها من الاستجابة هي رمز الحالة. يُعلمك رمز الحالة بحالة الطلب.
على سبيل المثال، تعني حالة 200 OK أن طلبك قد تم بنجاح، بينما تعني حالة 404 NOT FOUND أن المورد الذي تبحث عنه لم يتم العثور عليه. وهناك العديد من رموز الحالة الأخرى التي قد تُعطيك معلومات مُحددة حول ما حدث لطلبك.
من خلال الوصول إلى status_code.، يمكنك رؤية رمز الحالة الذي أعاده الخادم:
>>> response.status_code
200
أعاد status_code. القيمة 200، مما يعني أن طلبك كان ناجحًا وأن الخادم استجاب بالبيانات التي طلبتها.
في بعض الأحيان، قد ترغب في استخدام هذه المعلومات لاتخاذ قرارات في التعليمات البرمجية الخاصة بك:
if response.status_code == 200:
print("Success!")
elif response.status_code == 404:
print("Not Found.")
بناءً على هذا المنطق، إذا أعاد الخادم رمز حالة 200، فسيطبع برنامجك “تم بنجاح!”. أما إذا كانت النتيجة 404، فسيطبع برنامجك “غير موجود”.
تُسهّل مكتبة Requests هذه العملية أكثر. فإذا استخدمتَ كائن Response في سياق منطقي، كعبارة شرطية مثلاً، فسيتم تقييمه إلى True عندما يكون رمز الحالة أقل من 400، وإلى False في غير ذلك.
هذا يعني أنه يمكنك تعديل المثال الأخير عن طريق إعادة كتابة عبارة if:
if response:
print("Success!")
else:
raise Exception(f"Non-success status code: {response.status_code}")
في مقتطف الكود أعلاه، تتحقق ضمنيًا مما إذا كان رمز الحالة للاستجابة يقع بين 200 و 399. إذا لم يكن كذلك، فإنك تثير استثناءً مع رسالة خطأ تتضمن رمز حالة عدم النجاح ملفوفًا في سلسلة نصية.
ضع في اعتبارك أن هذه الطريقة لا تتحقق مما إذا كان رمز الحالة يساوي 200. وذلك لأن رموز الحالة الأخرى ضمن النطاق من 200 إلى 399، مثل 204 لا يوجد محتوى و304 لم يتم التعديل، تعتبر ناجحة أيضًا لأنها توفر استجابة قابلة للتنفيذ.
على سبيل المثال، يشير رمز الحالة 204 إلى أن الاستجابة كانت ناجحة، ولكن لا يوجد محتوى لإعادته في نص الرسالة.
لذا، تأكد من استخدام هذا الاختصار المريح فقط إذا كنت ترغب في معرفة ما إذا كان الطلب ناجحًا بشكل عام. بعد ذلك، إذا لزم الأمر، ستحتاج إلى التعامل مع الاستجابة بشكل مناسب بناءً على رمز الحالة.
لنفترض أنك لا ترغب في التحقق من رمز حالة الاستجابة في عبارة شرطية. بدلاً من ذلك، تريد استخدام إمكانيات Request المدمجة لرفع استثناء في حالة فشل الطلب. يمكنك فعل ذلك باستخدام ()raise_for_status:
import requests
from requests.exceptions import HTTPError
URLS = ["https://api.github.com", "https://api.github.com/invalid"]
for url in URLS:
try:
response = requests.get(url)
response.raise_for_status()
except HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
except Exception as err:
print(f"Other error occurred: {err}")
else:
print("Success!")
إذا قمت باستدعاء ()raise_for_status، فسيقوم Requests برفع استثناء HTTPError لرموز الحالة بين 400 و 600. إذا كان رمز الحالة يشير إلى طلب ناجح، فسيستمر البرنامج دون رفع هذا الاستثناء.
الآن، لديك معرفة واسعة بكيفية التعامل مع رمز حالة الاستجابة التي تتلقاها من الخادم. مع ذلك، عند إرسال طلب GET، نادرًا ما يقتصر اهتمامك على رمز الحالة فقط، بل غالبًا ما ترغب في رؤية المزيد. ستتعلم لاحقًا كيفية عرض البيانات الفعلية التي أرسلها الخادم في نص الاستجابة.
الوصول إلى محتوى الرد
غالباً ما تحتوي استجابة طلب GET على معلومات قيّمة، تُعرف باسم الحمولة، في نص الرسالة. باستخدام سمات وأساليب الاستجابة، يمكنك عرض الحمولة بتنسيقات متنوعة.
لعرض محتوى الاستجابة بالبايت، استخدم content.:
>>> import requests
>>> response = requests.get("https://api.github.com")
>>> response.content
b'{"current_user_url":"https://api.github.com/user", ...}'
>>> type(response.content)
<class 'bytes'>
بينما يتيح لك ملف content. الوصول إلى البايتات الخام لحمولة الاستجابة، سترغب غالبًا في تحويلها إلى سلسلة نصية باستخدام ترميز أحرف مثل UTF-8. سيقوم ملف response بذلك نيابةً عنك عند الوصول إلى ملف text.
>>> response.text
'{"current_user_url":"https://api.github.com/user", ...}'
>>> type(response.text)
<class 'str'>
لأن فك تشفير البايتات إلى سلسلة نصية يتطلب نظام تشفير، سيحاول Requests تخمين التشفير بناءً على رؤوس الاستجابة إذا لم تحدد تشفيرًا. يمكنك تحديد تشفير صريح عن طريق تعيين .encoding قبل الوصول إلى .text.
>>> response.encoding = "utf-8" # Optional: Requests infers this.
>>> response.text
'{"current_user_url":"https://api.github.com/user", ...}'
إذا ألقيت نظرة على الاستجابة، فسترى أنها في الواقع محتوى JSON مُسلسل. للحصول على قاموس، يمكنك أخذ السلسلة النصية التي استرجعتها من .text وفك تسلسلها باستخدام ()json.loads. ومع ذلك، فإن الطريقة المباشرة لإنجاز هذه المهمة هي استخدام ()json.
>>> response.json()
{'current_user_url': 'https://api.github.com/user', ...}
>>> type(response.json())
<class 'dict'>
نوع القيمة المُعادة من الدالة ()json هو قاموس، لذا يمكنك الوصول إلى القيم في الكائن باستخدام المفتاح:
>>> response_dict = response.json()
>>> response_dict["emojis_url"]
'https://api.github.com/emojis'
يمكنك فعل الكثير باستخدام رموز الحالة ومحتوى الرسائل. ولكن إذا كنت بحاجة إلى مزيد من المعلومات، مثل البيانات الوصفية حول الاستجابة نفسها، فسيتعين عليك الاطلاع على رؤوس الاستجابة.
عرض رؤوس الاستجابة
يمكن أن توفر لك رؤوس الاستجابة معلومات مفيدة، مثل نوع محتوى حمولة الاستجابة ومدة تخزين الاستجابة مؤقتًا. لعرض هذه الرؤوس، ادخل إلى .headers:
>>> import requests
>>> response = requests.get("https://api.github.com")
>>> response.headers
{'Server': 'github.com',
...
'X-GitHub-Request-Id': 'AE83:3F40:2151C46:438A840:65C38178'}
تُعيد السمة .headers كائنًا شبيهًا بالقاموس، مما يسمح لك بالوصول إلى قيم الترويسة باستخدام المفتاح. على سبيل المثال، لعرض نوع محتوى حمولة الاستجابة، يمكنك الوصول إلى “Content-Type”:
>>> response.headers["Content-Type"]
'application/json; charset=utf-8'
هناك شيء مميز في كائن الرؤوس هذا الذي يشبه القاموس. تُعرّف مواصفات HTTP الرؤوس بأنها غير حساسة لحالة الأحرف، مما يعني أنه يمكنك الوصول إليها دون القلق بشأن حالة الأحرف.
>>> response.headers["content-type"]
'application/json; charset=utf-8'
سواء استخدمت المفتاح “content-type” أو “Content-Type”، ستحصل على نفس القيمة.
بعد أن شاهدتَ أهمّ خصائص وأساليب الاستجابة أثناء العمل، أصبح لديك الآن فهمٌ جيّدٌ للاستخدام الأساسيّ للطلبات. يمكنك الحصول على المحتوى من الإنترنت والعمل مع الاستجابة التي تتلقّاها.
لكن الإنترنت ليس مجرد عناوين URL بسيطة ومباشرة. في القسم التالي، ستتعرف على كيفية تغير استجاباتك عند تخصيص طلبات GET لتشمل معلمات سلسلة الاستعلام.
إضافة معلمات سلسلة الاستعلام
إحدى الطرق الشائعة لتخصيص طلب GET هي تمرير القيم عبر معلمات سلسلة الاستعلام في عنوان URL. وللقيام بذلك باستخدام دالة ()get، يتم تمرير البيانات إلى المعلمات. على سبيل المثال، يمكنك استخدام واجهة برمجة تطبيقات البحث عن المستودعات في GitHub للبحث عن مستودعات بايثون الشائعة.
import requests
response = requests.get(
"https://api.github.com/search/repositories",
params={"q": "language:python", "sort": "stars", "order": "desc"},
)
json_response = response.json()
popular_repositories = json_response["items"]
for repo in popular_repositories[:3]:
print(f"Name: {repo['name']}")
print(f"Description: {repo['description']}")
print(f"Stars: {repo['stargazers_count']}\n")
من خلال تمرير قاموس إلى معلمة params الخاصة بـ ()get، يمكنك تعديل النتائج التي يتم إرجاعها من واجهة برمجة تطبيقات البحث.
يمكنك تمرير المعاملات إلى الدالة ()get إما كقاموس، كما فعلت للتو، أو كقائمة من الصفوف:
>>> import requests
>>> requests.get(
... "https://api.github.com/search/repositories",
... [("q", "language:python"), ("sort", "stars"), ("order", "desc")],
... )
<Response [200]>
بل يمكنك تمرير القيم كبايتات:
>>> requests.get(
... "https://api.github.com/search/repositories",
... params=b"q=language:python&sort=stars&order=desc",
... )
<Response [200]>
تُعدّ سلاسل الاستعلام مفيدة لتحديد معلمات طلبات GET. ومن الطرق الأخرى لتخصيص طلباتك إضافة أو تعديل الرؤوس التي ترسلها.
تخصيص رؤوس الطلبات
لتخصيص ترويسات HTTP، يمكنك تمرير قاموس ترويسات HTTP إلى دالة ()get باستخدام مُعامل headers. على سبيل المثال، يمكنك تغيير طلب البحث السابق لتمييز مصطلحات البحث المطابقة في النتائج عن طريق تحديد نوع الوسائط text-match في ترويسة Accept.
import requests
response = requests.get(
"https://api.github.com/search/repositories",
params={"q": '"real python"'},
headers={"Accept": "application/vnd.github.text-match+json"},
)
json_response = response.json()
first_repository = json_response["items"][0]
print(first_repository["text_matches"][0]["matches"])
يُخبر رأس Accept الخادم بأنواع المحتوى التي يمكن لتطبيقك معالجتها. في هذه الحالة، بما أنك تتوقع تمييز مصطلحات البحث المطابقة، فأنت تستخدم قيمة الرأس application/vnd.github.text-match+json. هذا رأس Accept خاص بـ GitHub، حيث يكون المحتوى بتنسيق JSON خاص.
عند تشغيل برنامج بايثون هذا، ستحصل على نتيجة مشابهة لتلك الموضحة أدناه:
$ python text_matches.py
[{'text': 'Real Python', 'indices': [23, 34]}]
قبل أن تتعلم المزيد من الطرق لتخصيص الطلبات، ستوسع آفاقك من خلال استكشاف طرق HTTP الأخرى.
استخدم طرق HTTP أخرى
إلى جانب طلب GET، تشمل طرق HTTP الشائعة الأخرى POST وPUT وDELETE وHEAD وPATCH وOPTIONS. توفر مكتبة Requests لكل طريقة من هذه الطرق دالة ذات توقيع مشابه لدالة ()get.
ستلاحظ أن مكتبة Requests توفر واجهة سهلة الاستخدام لجميع طرق HTTP الشائعة:
>>> import requests
>>> requests.get("https://httpbin.org/get")
<Response [200]>
>>> requests.post("https://httpbin.org/post", data={"key": "value"})
<Response [200]>
>>> requests.put("https://httpbin.org/put", data={"key": "value"})
<Response [200]>
>>> requests.delete("https://httpbin.org/delete")
<Response [200]>
>>> requests.head("https://httpbin.org/get")
<Response [200]>
>>> requests.patch("https://httpbin.org/patch", data={"key": "value"})
<Response [200]>
>>> requests.options("https://httpbin.org/get")
<Response [200]>
في المثال أعلاه، قمت باستدعاء كل دالة لإجراء طلب إلى خدمة httpbin باستخدام طريقة HTTP المقابلة.
جميع هذه الدوال هي اختصارات عالية المستوى لـ ()requests.request، والتي تأخذ اسم الدالة كمعاملها الأول:
>>> requests.request("GET", "https://httpbin.org/get")
<Response [200]>
يمكنك استخدام استدعاء الدالة المكافئة ذات المستوى الأدنى، لكن تكمن قوة مكتبة Requests في بايثون في واجهتها سهلة الاستخدام وعالية المستوى. يمكنك فحص الاستجابات تمامًا كما كنت تفعل سابقًا.
>>> response = requests.head("https://httpbin.org/get")
>>> response.headers["Content-Type"]
'application/json'
>>> response = requests.delete("https://httpbin.org/delete")
>>> json_response = response.json()
>>> json_response["args"]
{}
بغض النظر عن الطريقة التي تستخدمها، ستحصل على كائن استجابة يوفر الوصول إلى الرؤوس، ونصوص الاستجابة، ورموز الحالة، والمزيد.
بعد ذلك، ستلقي نظرة فاحصة على طرق POST و PUT و PATCH وتتعرف على كيفية اختلافها عن أنواع الطلبات الأخرى.
إرسال بيانات الطلب
وفقًا لمواصفات بروتوكول HTTP، تُمرر طلبات POST وPUT، وطلبات PATCH الأقل شيوعًا، بياناتها عبر نص الرسالة بدلًا من تمريرها عبر معلمات في سلسلة الاستعلام. أما في مكتبة Requests، فتُمرر هذه البيانات إلى مُعامل البيانات الخاص بالدالة المُناسبة.
تأخذ مُعاملة البيانات قاموسًا، أو قائمة من الصفوف، أو بايتات، أو كائنًا شبيهًا بالملف. ستحتاج إلى تعديل البيانات التي تُرسلها في نص طلبك لتناسب الاحتياجات الخاصة للخدمة التي تتفاعل معها.
على سبيل المثال، إذا كان نوع محتوى طلبك هو application/x-www-form-urlencoded، فيمكنك إرسال بيانات النموذج كقاموس:
>>> import requests
>>> requests.post("https://httpbin.org/post", data={"key": "value"})
<Response [200]>
يمكنك أيضًا إرسال نفس البيانات كقائمة من الصفوف:
>>> requests.post("https://httpbin.org/post", data=[("key", "value")])
<Response [200]>
إذا كنت بحاجة إلى إرسال بيانات JSON، فيمكنك استخدام مُعامل json. عند تمرير بيانات JSON عبر json، سيقوم Requests بتحويل بياناتك إلى سلسلة نصية وإضافة رأس Content-Type الصحيح تلقائيًا.
كما تعلمت سابقًا، تقبل خدمة httpbin طلبات الاختبار وتستجيب ببيانات حول هذه الطلبات. على سبيل المثال، يمكنك استخدامها لفحص طلب POST أساسي:
>>> response = requests.post("https://httpbin.org/post", json={"key": "value"})
>>> json_response = response.json()
>>> json_response["data"]
'{"key": "value"}'
>>> json_response["headers"]["Content-Type"]
'application/json'
يمكنك ملاحظة من الاستجابة أن الخادم قد استلم بيانات طلبك وعناوينه كما أرسلتها تمامًا. كما توفر لك مكتبة Requests هذه المعلومات في شكل PreparedRequest، والذي ستتناوله بمزيد من التفصيل في القسم التالي.
افحص الطلب المُعدّ
عند إرسال طلب، تقوم مكتبة Requests بتجهيز الطلب قبل إرساله فعليًا إلى الخادم الوجهة. يشمل تجهيز الطلب أمورًا مثل التحقق من صحة العناوين وتحويل محتوى JSON إلى سلسلة نصية.
يمكنك عرض كائن PreparedRequest من خلال الوصول إلى request. في كائن Response:
>>> import requests
>>> response = requests.post("https://httpbin.org/post", json={"key":"value"})
>>> response.request
<PreparedRequest [POST]>
>>> response.request.headers["Content-Type"]
'application/json'
>>> response.request.url
'https://httpbin.org/post'
>>> response.request.body
b'{"key": "value"}'
يمنحك فحص PreparedRequest إمكانية الوصول إلى جميع أنواع المعلومات المتعلقة بالطلب الذي يتم تقديمه، مثل الحمولة وعنوان URL والعناوين والمصادقة والمزيد.
حتى الآن، قمتَ بالعديد من أنواع الطلبات المختلفة، ولكن جميعها تشترك في شيء واحد: أنها طلبات غير مصادق عليها إلى واجهات برمجة التطبيقات العامة. ستطلب منك العديد من الخدمات التي ستصادفها المصادقة بطريقة أو بأخرى.
استخدم المصادقة
تساعد عملية المصادقة الخدمة على فهم هويتك. عادةً، تُقدّم بيانات اعتمادك إلى الخادم عبر تمرير البيانات من خلال ترويسة Authorization أو ترويسة مخصصة تُحددها الخدمة. جميع الدوال في Requests التي اطلعت عليها حتى الآن تُوفّر مُعاملًا يُسمى auth، والذي يسمح لك بتمرير بيانات اعتمادك مباشرةً.
>>> import requests
>>> response = requests.get(
... "https://httpbin.org/basic-auth/user/passwd",
... auth=("user", "passwd")
... )
>>> response.status_code
200
>>> response.request.headers["Authorization"]
'Basic dXNlcjpwYXNzd2Q='
ينجح الطلب إذا كانت بيانات الاعتماد التي تمررها في المجموعة إلى المصادقة صالحة.
عندما تقوم بتمرير بيانات الاعتماد الخاصة بك في مجموعة إلى معلمة المصادقة، يقوم Requests بتطبيق بيانات الاعتماد باستخدام مخطط مصادقة الوصول الأساسي لـ HTTP في الخلفية.
يمكنك تقديم الطلب نفسه عن طريق تمرير بيانات اعتماد المصادقة الأساسية الصريحة باستخدام HTTPBasicAuth:
>>> from requests.auth import HTTPBasicAuth
>>> requests.get(
... "https://httpbin.org/basic-auth/user/passwd",
... auth=HTTPBasicAuth("user", "passwd")
... )
<Response [200]>
على الرغم من أنك لست بحاجة إلى تحديد طريقة مصادقة صريحة للمصادقة الأساسية، فقد ترغب في المصادقة باستخدام طريقة أخرى. توفر مكتبة Requests طرق مصادقة أخرى جاهزة للاستخدام، مثل HTTPDigestAuth وHTTPProxyAuth.
من الأمثلة الواقعية على واجهات برمجة التطبيقات التي تتطلب المصادقة، واجهة برمجة تطبيقات المستخدم المصادق عليه في GitHub. توفر هذه الواجهة معلومات حول ملف تعريف المستخدم المصادق عليه.
إذا حاولت تقديم طلب بدون بيانات اعتماد، فسترى أن رمز الحالة هو 401 غير مصرح به:
>>> requests.get("https://api.github.com/user")
<Response [401]>
إذا لم تقدم بيانات اعتماد المصادقة عند الوصول إلى خدمة تتطلبها، فستحصل على رمز خطأ HTTP كاستجابة.
لإجراء طلب إلى واجهة برمجة تطبيقات المستخدم المصادق عليه في GitHub، تحتاج أولاً إلى إنشاء رمز وصول شخصي بنطاق القراءة: المستخدم. بعد ذلك، يمكنك تمرير هذا الرمز كعنصر ثانٍ في مجموعة إلى دالة ()get:
>>> import requests
>>> token = "<YOUR_GITHUB_PA_TOKEN>"
>>> response = requests.get(
... "https://api.github.com/user",
... auth=("", token)
... )
>>> response.status_code
200
كما تعلمت سابقًا، يقوم هذا الأسلوب بتمرير بيانات الاعتماد إلى HTTPBasicAuth، الذي يتوقع اسم مستخدم وكلمة مرور، ويرسل بيانات الاعتماد كسلسلة مشفرة بنظام Base64 مع البادئة “Basic”:
>>> response.request.headers["Authorization"]
'Basic OmdocF92dkd...WpremM0SGRuUGY='
هذه الطريقة تعمل، لكنها ليست الطريقة الصحيحة للمصادقة باستخدام رمز Bearer – واستخدام سلسلة نصية فارغة لإدخال اسم المستخدم الزائد أمر غير مريح.
باستخدام Requests، يمكنك توفير آلية مصادقة خاصة بك لحل هذه المشكلة. لتجربة ذلك، أنشئ فئة فرعية من AuthBase ونفّذ الدالة .__call__():
from requests.auth import AuthBase
class TokenAuth(AuthBase):
"""Implements a token authentication scheme."""
def __init__(self, token):
self.token = token
def __call__(self, request):
"""Attach an API token to the Authorization header."""
request.headers["Authorization"] = f"Bearer {self.token}"
return request
هنا، تستقبل آلية TokenAuth المخصصة الخاصة بك رمزًا مميزًا، ثم تقوم بتضمين هذا الرمز المميز في رأس Authorization الخاص بطلبك، كما تقوم بتعيين البادئة “Bearer” الموصى بها للسلسلة.
يمكنك الآن استخدام مصادقة الرمز المميز المخصصة هذه لإجراء مكالمتك إلى واجهة برمجة تطبيقات المستخدم المصادق عليه في GitHub:
>>> import requests
>>> from custom_token_auth import TokenAuth
>>> token = "<YOUR_GITHUB_PA_TOKEN>"
>>> response = requests.get(
... "https://api.github.com/user",
... auth=TokenAuth(token)
... )
>>> response.status_code
200
>>> response.request.headers["Authorization"]
'Bearer ghp_b...Tx'
لقد أنشأت دالة TokenAuth المخصصة سلسلة نصية منسقة بشكل جيد لعنوان Authorization. وهذا يوفر لك طريقة أكثر سهولة وقابلية لإعادة الاستخدام للعمل مع أنظمة المصادقة القائمة على الرموز المميزة، مثل تلك المطلوبة في أجزاء من واجهة برمجة تطبيقات GitHub.
قد تؤدي آليات المصادقة الضعيفة إلى ثغرات أمنية. ما لم تتطلب الخدمة آلية مصادقة مخصصة لسبب ما، فمن الأفضل الالتزام بطريقة مجربة وموثوقة، مثل المصادقة الأساسية المدمجة أو OAuth، على سبيل المثال، عبر مكتبة Requests-OAuthlib.
أثناء تفكيرك في الأمن، ضع في اعتبارك التعامل مع شهادات TLS/SSL باستخدام Requests.
التواصل الآمن مع الخوادم
عندما تكون البيانات التي تحاول إرسالها أو استقبالها حساسة، يصبح الأمن ضروريًا. يتم التواصل مع المواقع الآمنة عبر بروتوكول HTTP من خلال إنشاء اتصال مشفر باستخدام بروتوكول أمان طبقة النقل (TLS). يُعدّ TLS خليفةً لبروتوكول طبقة المقابس الآمنة (SSL)، حيث يوفر أمانًا وكفاءةً مُحسّنين في الاتصالات الآمنة. مع ذلك، لا يزال من الشائع بين المبرمجين استخدام مصطلح SSL بدلًا من TLS.
يقوم Requests بالتحقق من الشهادات الرقمية للخوادم تلقائيًا، ونادرًا ما تحتاج إلى تعديل هذا السلوك. مع ذلك، قد تحتاج في بعض الحالات إلى تخصيص عملية التحقق.
على سبيل المثال، عندما تعمل في بيئة مؤسسية مع سلطات إصدار شهادات مخصصة، قد تحتاج إلى توفير حزمة الشهادات الخاصة بك:
>>> import requests
>>> requests.get(
... "https://internal-api.company.com",
... verify="/path/to/company-ca.pem"
... )
<Response [200]>
يمكنك أيضًا توفير دليل يحتوي على ملفات الشهادات إذا كنت بحاجة إلى الوثوق بالعديد من السلطات المخصصة.
إذا كنت تقوم بتصحيح أخطاء الشهادات في بيئة التطوير، فقد تميل إلى تعطيل التحقق تمامًا. مع أن هذا الحل قد ينجح، إلا أنه يشكل خطرًا أمنيًا كبيرًا، ويجب عليك تجنب استخدامه في بيئة الإنتاج.
>>> requests.get("https://api.github.com", verify=False)
InsecureRequestWarning: Unverified HTTPS request is being made to host
⮑ 'api.github.com'. Adding certificate verification is strongly advised.
⮑ See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
⮑ warnings.warn(
<Response [200]>
يحذرك Requests من هذه الممارسة الخطيرة لأن تعطيل التحقق يجعلك عرضة لهجمات الوسيط.
الآن بعد أن عرفت كيفية التعامل مع التحقق من الشهادات بشكل آمن، قد تتساءل عن كيفية الحفاظ على تشغيل برنامجك بأسرع ما يمكن.
تحسين الأداء
عند استخدام مكتبة Requests، وخاصة في بيئة تطبيقات الإنتاج، من المهم مراعاة تأثير ذلك على الأداء. تساعد ميزات مثل التحكم في مهلة الاستجابة، والجلسات، وحدود إعادة المحاولة في ضمان تشغيل تطبيقك بسلاسة.
ملاحظة: لا تدعم مكتبة Requests طلبات HTTP غير المتزامنة بشكل مباشر. إذا كنت بحاجة إلى دعم غير متزامن في برنامجك، فيُرجى تجربة AIOHTTP أو HTTPX. تتوافق المكتبة الأخيرة بشكل عام مع صيغة Requests.
حتى في التعليمات البرمجية المتزامنة، هناك العديد من التقنيات المهمة التي يمكنك استخدامها لتحسين طلبات HTTP الخاصة بك ومنع حدوث اختناقات في الأداء.
ضبط مهلة الطلبات
عند إرسال طلب مباشر إلى خدمة خارجية، يجب على نظامك انتظار الرد قبل المتابعة. إذا انتظر تطبيقك وقتًا طويلاً للحصول على هذا الرد، فقد تتراكم الطلبات إلى خدمتك، وقد تتأثر تجربة المستخدم سلبًا، أو قد تتوقف العمليات التي تعمل في الخلفية.
بشكل افتراضي، تنتظر الطلبات الرد إلى أجل غير مسمى، لذا يُنصح دائمًا بتحديد مدة مهلة زمنية لتجنب هذه المشكلات. لضبط مهلة الطلب، استخدم مُعامل المهلة. يمكن أن تكون قيمة المهلة عددًا صحيحًا أو عشريًا يُمثل عدد الثواني التي يجب انتظار الرد فيها قبل انتهاء المهلة.
>>> requests.get("https://api.github.com", timeout=1)
<Response [200]>
>>> requests.get("https://api.github.com", timeout=0.01)
Traceback (most recent call last):
...
requests.exceptions.ConnectTimeout:
⮑ HTTPSConnectionPool(host='api.github.com', port=443):
⮑ Max retries exceeded with url: / (Caused by ConnectTimeoutError(...))
اكتمل الطلب الأول بنجاح لأن الخادم استجاب خلال مهلة ثانية واحدة. في المقابل، فشل الطلب الثاني لأن مهلة الانتظار كانت قصيرة للغاية، عشرة أجزاء من الألف من الثانية، والتي انقضت قبل إنشاء الاتصال.
يمكنك أيضًا تمرير مجموعة بيانات إلى دالة timeout تحتوي على العنصرين التاليين:
- مهلة الاتصال: مقدار الوقت الذي يسمح به للعميل لإنشاء اتصال بالخادم
- مهلة القراءة: الوقت الذي ينتظره النظام للحصول على رد بعد أن يُنشئ العميل اتصالاً.
يجب أن يكون كلا العنصرين أرقامًا، ويمكن أن يكونا من النوع int أو float:
>>> requests.get("https://api.github.com", timeout=(3.05, 5))
<Response [200]>
إذا تم إنشاء اتصال خلال 3.05 ثانية واستلمت البيانات خلال 5 ثوانٍ من إنشاء الاتصال، فسيتم إرجاع الاستجابة كما كانت سابقًا. أما إذا انتهت مهلة الطلب، فستُطلق الدالة إما استثناء ConnectTimeout أو ReadTimeout، وهما نوعان فرعيان من استثناء Timeout الأكثر عمومية.
import requests
from requests.exceptions import Timeout
try:
response = requests.get("https://api.github.com", timeout=(3.05, 5))
except Timeout:
print("The request timed out")
else:
print("The request did not time out")
يمكن لبرنامجك التقاط استثناء انتهاء المهلة والاستجابة وفقًا لذلك.
إعادة استخدام الاتصالات مع كائنات الجلسة
حتى الآن، كنت تتعامل مع واجهات برمجة تطبيقات الطلبات عالية المستوى مثل ()get و ()post. هذه الدوال هي تجريدات لما يحدث عند إرسال طلباتك. فهي تخفي تفاصيل التنفيذ، مثل كيفية إدارة الاتصالات، لذا لا داعي للقلق بشأنها.
يوجد أسفل هذه التجريدات فئة تُسمى Session. إذا كنت بحاجة إلى ضبط تحكمك بدقة في كيفية إرسال الطلبات أو تحسين أداء طلباتك، فقد تحتاج إلى استخدام مثيل Session مباشرةً.
تُستخدم الجلسات لحفظ المعلمات عبر الطلبات. على سبيل المثال، إذا كنت ترغب في استخدام نفس بيانات المصادقة عبر طلبات متعددة، فيمكنك استخدام جلسة:
import requests
from custom_token_auth import TokenAuth
TOKEN = "<YOUR_GITHUB_PA_TOKEN>"
with requests.Session() as session:
session.auth = TokenAuth(TOKEN)
first_response = session.get("https://api.github.com/user")
second_response = session.get("https://api.github.com/user")
print(first_response.headers)
print(second_response.json())
في هذا المثال، تستخدم مدير السياق لضمان تحرير الجلسة للموارد عندما لا تحتاج إليها بعد الآن.
في السطر 7، تقوم بربط بيانات اعتماد مستخدم GitHub الخاص بك بكائن الجلسة باستخدام TokenAuth المخصص. يكفي القيام بذلك مرة واحدة فقط لكل جلسة، وبعدها يمكنك إرسال طلبات مصادقة متعددة. ستحتفظ الطلبات ببيانات الاعتماد طوال مدة الجلسة.
في السطرين 9 و 10، تقوم بعد ذلك بإجراء طلبين إلى واجهة برمجة تطبيقات المستخدم المصادق عليه باستخدام ()session.get بدلاً من ()requests.get.
يتمثل التحسين الأساسي لأداء الجلسات في الاتصالات المستمرة. فعندما يتصل تطبيقك بخادم باستخدام جلسة، فإنه يحتفظ بهذا الاتصال في مجمع اتصالات. وعندما يرغب تطبيقك في الاتصال بالخادم نفسه مرة أخرى، فإنه يعيد استخدام اتصال من المجمع بدلاً من إنشاء اتصال جديد.
إعادة محاولة الطلبات الفاشلة
عند فشل طلب ما، قد ترغب في أن يعيد تطبيقك محاولة إرسال الطلب نفسه. مع ذلك، لا يقوم Requests بذلك تلقائيًا. لتطبيق هذه الخاصية، عليك إنشاء مُهايئ نقل مخصص.
تتيح لك محولات النقل تحديد مجموعة من الإعدادات لكل خدمة تتفاعل معها. على سبيل المثال، لنفترض أنك تريد إعادة محاولة جميع الطلبات إلى https://api.github.com مرتين قبل ظهور خطأ إعادة المحاولة. في هذه الحالة، ستقوم بإنشاء محول نقل، وتعيين قيمة المعامل max_retries الخاص به، وربطه بجلسة موجودة.
import requests
from requests.adapters import HTTPAdapter
from requests.exceptions import RetryError
from urllib3.util.retry import Retry
retry_strategy = Retry(
total=2,
status_forcelist=[429, 500, 502, 503, 504]
)
github_adapter = HTTPAdapter(max_retries=retry_strategy)
with requests.Session() as session:
session.mount("https://api.github.com", github_adapter)
try:
response = session.get("https://api.github.com/")
except RetryError as err:
print(f"Error: {err}")
في هذا المثال، قمتَ بإعداد جلستك بحيث تُعيد المحاولة مرتين كحد أقصى إذا لم يعمل طلبك إلى واجهة برمجة تطبيقات GitHub كما هو متوقع. يمنحك كائن إعادة المحاولة تحكمًا دقيقًا في رموز الحالة التي تُؤدي إلى إعادة المحاولة.
تأخذ الوسيطة max_retries إما عددًا صحيحًا أو كائن Retry. يمنحك استخدام كائن Retry تحكمًا دقيقًا في عمليات إعادة محاولة معالجة حالات الفشل. مع أنه لا يزال بإمكانك تمرير عدد صحيح بسيط للحالات الأساسية، إلا أن استخدام كائن Retry هو الأسلوب الحديث.
عند تثبيت HTTPAdapter على الجلسة، ستلتزم الجلسة بتكوينها لكل طلب إلى https://api.github.com.
ملاحظة: على الرغم من أن التنفيذ الموضح أعلاه يعمل، فلن ترى أي آثار لسلوك إعادة المحاولة إلا إذا كان هناك خطأ ما في اتصال الشبكة الخاص بك أو خوادم GitHub.
إذا كنت ترغب في تجربة الكود المبني على هذا المثال ومعرفة متى تحدث عمليات إعادة المحاولة، فأنت محظوظ.
يُحسّن الكود الموجود في هذا الملف المثال الموضح أعلاه بإضافة تسجيل لعرض مخرجات تصحيح الأخطاء. وهذا يُتيح لك مراقبة محاولات إعادة تنفيذ أوامر بايثون.
تأتي مكتبة Requests مزودة بتطبيقات بديهية للمهل الزمنية ومحولات النقل والجلسات التي يمكن أن تساعدك في الحفاظ على كفاءة التعليمات البرمجية الخاصة بك ومرونة تطبيقك.
عمل رائع! لقد وصلت إلى نهاية البرنامج التعليمي وقطعت شوطاً طويلاً في توسيع معرفتك بمكتبة Requests القوية في بايثون.
لأنك تعلمت كيفية استخدام Requests، فأنت مجهز لاستكشاف عالم خدمات الويب الواسع – وبناء تطبيقات رائعة باستخدام البيانات الرائعة التي توفرها.
الأسئلة الشائعة
الآن وقد اكتسبت بعض الخبرة في استخدام مكتبة Requests الخاصة بلغة بايثون، يمكنك استخدام الأسئلة والأجوبة أدناه للتحقق من فهمك ومراجعة ما تعلمته.
تتعلق هذه الأسئلة الشائعة بأهم المفاهيم التي تم شرحها في هذا الدرس. انقر على زر إظهار/إخفاء بجوار كل سؤال لعرض الإجابة.
لا، مكتبة Requests ليست جزءًا من مكتبة بايثون القياسية. عليك تثبيتها بشكل منفصل باستخدام pip.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.
يمكنك إجراء طلب GET في بايثون باستخدام الدالة ()requests.get، مع تمرير عنوان URL المطلوب كوسيط.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.
يمكنك إرسال بيانات POST باستخدام Requests عن طريق تمرير البيانات إلى مُعامل data في دالة ()requests.post. أما بالنسبة لبيانات JSON، فاستخدم مُعامل json بدلاً من ذلك.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.
يمكنك إضافة رؤوس إلى الطلبات في بايثون عن طريق تمرير قاموس من الرؤوس إلى معلمة الرؤوس في وظائف ()requests.get أو ()requests.post.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.