مكتبة requests في البايثون - كيف تُرسل طلبات HTTP من بايثون

مكتبة requests في البايثون – كيف تُرسل طلبات HTTP من بايثون

مكتبة requests في البايثون هي أحد أشهر المكتبات التي تُستخدم في العديد من المشاريع الخاصة بالويب. تكاد لا تخلو أي مكتبة لها علاقة بالويب من استخدام هذه المكتبة الرائعة والمهمة، والتي تُسهل علينا التعامل مع بروتوكول الويب المشهور HTTP. أهلا بكم في بايثونات في هذا المقال الجديد الذي نتناول فيه مكتبة جديدة من مكتبات بايثون الرائعة.

أجرينا خلال الشهر السابق استطلاعًا للرأي، طلبنا فيه من جمهور بايثونات اختيار مكتبة بايثون من بين مجموعة من أشهر مكتبات بايثون. وحصلت فيه مكتبة requests على 40% من الأصوات. ونزولًا عند رغبة قراءِنا الأفاضل وجمهورنا العزيز، نقدم لكم هذا المقال.

استطلاع للرأي - بايثونات

عند نهاية هذا المقال، سنكون تعرفنا على:

  • ما هي مكتبة requests ؟
  • كيف نُثبت مكتبة requests ؟
  • إرسال طلبات GET
  • تفاصيل الرد Response
  • تطبيق عملي على استخدام مكتبة requests

لنبدأ مقالنا على بركة الله.

ما هي مكتبة requests ؟

هي مكتبة في لُغة بايثون تُمكننا من التعامل مع بروتوكول HTTP بكل سهولة من حيث ارسال الطلبات requests واستقبال الردود وإجراء إعدادات متقدمة فيهما. بُنيت هذه المكتبة لتكون مألوفة للبشر كما يَذكر التوثيق الخاص بها:

Requests is an elegant and simple HTTP library for Python, built for human beings.

تتضمن المكتبة العديد من الخيارات والإمكانات التي تُساعدنا في إعداد روابط url وإسناد المعاملات لها، وتخصيص مكونات الطلب، مثل تحديد نوع البيانات المُرسلة أو التي يجب استقبالها، وتخصيص محتوى العناوين في الطلب headers إلى غير ذلك من الإعدادات.

كيف نُثبت مكتبة requests ؟

قبل تثبيت المكتبة نتأكد من تحديث مدير الحزم في بايثون pip عبر تنفيذ الأمر التالي في الطرفية:

pip install --upgrade pip

نتأكد من نُسخة pip المُحدثة من خلال الأمر التالي في الطرفية أيضًا:

pip --version

بعد أن انتهينا من تحديث مُدير الحزم، يُمكننا الأن تثبيت مكتبة requests من خلال مُدير الحزم باستخدام الأمر التالي في الطرفية:

python -m pip install requests

أنواع الطلبات في بروتوكول HTTP

كما ذكرنا في مقدمة المقال، فإن مكتبة requests هي مكتبة خاصة ببروتوكول HTTP، والذي هو عبارة عن بروتوكل لنقل البيانات بين جهتين، الأولى هي العميل client والثانية هي الخادم server. يُسمى البروتوكول بــ “بروتوكول نقل النص التشعبي Hyper Text Transfer Protocol“. الوظيفة الرئيسية للبروتوكول هي نقل البيانات بكافة أنواعها مثل النصوص، الملفات، الصور، الفيديو وغيره من أنواع البيانات.

بروتوكول http
بروتوكول http – (المصدر)

الآلية الرئيسية لعمل البروتوكول تعتمد على مفهوم الطلب/الرد، وعليه يُوجد لدينا مجموعة من أنواع الطلبات التي يُمكن تحديدها في كُل طلب يتم ارساله للخادم. أنواع الطلبات هي كالتالي:

  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • TRACE
  • OPTIONS
  • CONNECT
  • PATCH

تُعتبر الطلبات من نوع GET و POST و PUT و DELETE هي الأكثر استخدمًا من الناحية العملية، مع وجود تطبيقات واستخدامات للأنواع الأخرى. لن نتطرق في هذا المقال إلى شرح البروتوكول وأنواع الطلبات المستخدمة فيه، ويُمكنكم الإطلاع أكثر على الموضوع من خلال المصادر التالية:

Hypertext Transfer Protocol

HTTP Protocol

إرسال طلبات GET

أمر GET هو أحد أكثر طلبات HTTP شيوعًا، ويعني عند استخدامه هو أنك تطلب بيانات معينة من الخادم او الجهة التي تُرسل إليها الطلب. ببساطة، فإننا عندما نفتح المتصفح، ونكتب عنوان موقع بايثونات فيه ثم Enter، فهذا يعني أننا أرسلنا طلب GET من خلال بروتوكول HTTP إلى الخادم الذي يستضيف الموقع نطلب فيه بيانات الصفحة الرئيسية للموقع على شكل مستند Html. يتم إرجاع البيانات على شكل رد Response يتضمن البيانات التي طلبناها بالاضافة لمعلومات أخرى يستفيد منها المتصفح في عرض البيانات بطريقة صحيحة وآمنة.

لإرسال طلب GET من خلال البايثون، وعبر مكتبة requests، نستورد المكتبة في البداية، ثم نُعرف متغير باسم response ونُسند له القيمة المُرجعة من استدعاء دالة get في المكتبة:

import requests as re

response = re.get('https://pythonat.com/')

print(type(response))
# result => <class 'requests.models.Response'>

print(response.status_code)
# result => 200

نُمرر للدالة get عنوان المصدر الخاص بالبيانات التي نطلبها على شكل url نصية. في المثال الأعلى، مررنا عنوان موقع بايثونات، ثم تعرفنا على نوع الرد والذي هو عبارة عن كائن من نوع <class ‘requests.models.Response’> وقمنا بطباعة حالة الرد والتي كانت 200 وتعني أن الطلب تم استقباله بنجاح وفهمه والرد عليه بنجاح.

من المهم التعرف على أكواد/أرقام الردود التي تكون موجودة في الرد. الرابط التالي يحتوي كافة أرقام الردود المستخدمة في بروتوكول HTTP:

HTTP status codes

تفاصيل الرد Response

في مثالنا السابق، استطعنا ارسال طلب GET للخادم الذي يستضيف موقع بايثونات، واستقبلنا الرد بنجاح، ولكن ما هي تفاصيل هذا الرد في البايثون؟ وما هي طبيعة البيانات الموجودة فيه؟

نستطيع التعرف أكثر على تفاصيل الرد من خلال التوابع الخاصة بالكائن من نوع Response، ونختصر أهمها في الجدول التالي:

التابعنوع التابعالوصف
contentخاصيةمحتوى الرد بصيغة bytes
cookiesخاصيةملفات الارتباط العائدة من الخادم خلال الرد
encodingخاصيةنوع الترميز، ويتم تحديده قبل استخدام خاصية text
headersخاصيةفهرس/قاموس من البيانات التي تُمثل العناوين الموجودة في الرد
jsonدالةتحويل محتوى الرد على شكل بيانات من نوع جيسون
status_codeخاصيةكود الرد
textخاصيةالمحتوى بصيغة نصية

محتوى Response

من خلال الجدول السابق، يُمكننا الحصول على البيانات المُرسلة في الرد وذلك من خلال عدة خصائص. الخاصية الأولى هي content:

import requests as re

response = re.get('https://pythonat.com/')

print(response.content)

# result => '<!DOCTYPE html> <!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7" dir="rtl" lang="ar"> ........

نتيجة تنفيذ الشيفرة البرمجية السابقة هي طباعة محتوى الرد الذي هو عبارة عن الصفحة الرئيسية لموقع بايثونات على شكل كود Html. لو غيرنا عنوان مصدر البيانات ليكون https://api.github.com بدلًا من https://pythonat.com/، ستكون البيانات المُرجعة عبارة عن بيانات json:

import requests as re

response = re.get('https://api.github.com/')

print(response.content)

# result => b'{\n  "current_user_url": "https://api.github.com/user",\n  "current_user_authorizations_html_url":......

اذا كانت البيانات المُرجعة في الرد على هيئة json، فيمكن حينها الحصول عليها بنفس النوع من خلال الدالة json كما يلي:

import requests as re

response = re.get('https://api.github.com/')

print(response.json())

''' result 
{'current_user_url': 'https://api.github.com/user',
 'current_user_authorizations_html_url': 'https://github.com/settings/connections/applications{/client_id}',
 'authorizations_url': 'https://api.github.com/authorizations',
 'code_search_url': 'https://api.github.com/search/code?q={query}{&page,per_page,sort,order}',
 'commit_search_url': 'https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}',
 'emails_url': 'https://api.github.com/user/emails',
 'emojis_url': 'https://api.github.com/emojis',
 'events_url': 'https://api.github.com/events',
 'feeds_url': 'https://api.github.com/feeds',
 'followers_url': 'https://api.github.com/user/followers',
 'following_url': 'https://api.github.com/user/following{/target}',
 'gists_url': 'https://api.github.com/gists{/gist_id}',
 'hub_url': 'https://api.github.com/hub',
 'issue_search_url': 'https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}',
 'issues_url': 'https://api.github.com/issues',
 'keys_url': 'https://api.github.com/user/keys',
 'label_search_url': 'https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}',
 'notifications_url': 'https://api.github.com/notifications',
 'organization_url': 'https://api.github.com/orgs/{org}',
 'organization_repositories_url': 'https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}',
 'organization_teams_url': 'https://api.github.com/orgs/{org}/teams',
 'public_gists_url': 'https://api.github.com/gists/public',
 'rate_limit_url': 'https://api.github.com/rate_limit',
 'repository_url': 'https://api.github.com/repos/{owner}/{repo}',
 'repository_search_url': 'https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}',
 'current_user_repositories_url': 'https://api.github.com/user/repos{?type,page,per_page,sort}',
 'starred_url': 'https://api.github.com/user/starred{/owner}{/repo}',
 'starred_gists_url': 'https://api.github.com/gists/starred',
 'user_url': 'https://api.github.com/users/{user}',
 'user_organizations_url': 'https://api.github.com/user/orgs',
 'user_repositories_url': 'https://api.github.com/users/{user}/repos{?type,page,per_page,sort}',
 'user_search_url': 'https://api.github.com/search/users?q={query}{&page,per_page,sort,order}'} 
 '''

على الأغلب، وفي البيئات التشغيلية الحقيقية، سنحتاج لمعرفة نوع البيانات قبل أن نتعامل معها، وقد نحتاج أيضًا إلى أن نُحدد نوع البيانات التي نُريد أن نستقبلها، فمثلا، قد نحتاج لبناء دالة او وظيفة تتعامل مع بيانات عبر الانترنت من خلال مكتبة requests وأن تكون نوع البيانات في الرد هي json وليست Html.

سنتمكن من معرفة نوع البيانات في الرد من خلال Headers الخاصة بالرد والتي سنتكلم عنها في العنوان القادم. لقراءة البيانات على شكل نصي نستخدم الخاصية text:

print(response.text)

في بعض الأحيان، يكون الرد على الطلب عبارة عن خطأ، مثل الخطأ المشهور “الصفحة المطلوبة غير موجودة” والذي يُرمز له بالرمز 404. في هذه الحالة يُمكننا من خلال مكتبة requests التعرف على كود الرد من خلال الخاصية status_code:

import requests as re

response = re.get('https://pythonat.com/page1/')

if response.status_code == 200:
	print('Request is successfully processed')
elif respose.status_code == 404:
	print('The requested page not found')
else:
	print('Other error')
	print(response.status_code)
	
# result will be 'The requested page not found' because there is no page1 on pythonat.com

خاصية headers

تتضمن خاصية headers معلومات مهمة عن طبيعة الرد وطبيعة البيانات، وتكون نتيجة هذه الخاصية عبارة عن مجموعة من المفاتيح والقيم (مثل القاموس). يُمكننا من خلال خاصية headers التعرف على نوع البيانات في الرد، فمثلا، العناون الخاص بموقع بايثونات يُعيد رد من نوع text/html أما العنوان https://api.github.com فإنه يُعيد بيانات من نوع json:

import requests as re

response1 = re.get('https://pythonat.com/')

print(response1.headers['Content-Type'])
# result => 'text/html; charset=UTF-8'

response2 = re.get('https://api.github.com/')

print(response2.headers['Content-Type'])
# result => 'application/json; charset=utf-8'

تطبيق عملي على استخدام مكتبة requests

بما أنه يُمكننا، برمجيًا، ارسال طلب لخادمٍ ما، ثم استقبال الرد ومعرفة تفاصيله من خلال مكتبة requests، فالمجال مفتوح أمامنا لتطبيق هذه الامكانية والاستفادة منها في العديد من التطبيقات، وحل الكثير من المشاكل والصعوبات التي نواجهها. في الجزء الأخير من هذا المقال، نعمل على استخدام مكتبة requests في جمع عناوين البريد الإلكتروني من صفحات مواقع معينة.

المثال هنا مشهور، وسوف نُبسطه للغاية، بحيث نقتصر ونوضح دور مكتبة requests. في البداية سنستورد المكتبات اللازمة للعمل وهي كالتالي:

import requests
import re

مكتبة re هي مكتبة بايثون للتعابير النمطية regular expression وسنستخدمها للبحث عن نمط البريد الإلكتروني في الصفحات الموجودة لدينا.

نُعرف قائمة باسم pages تحتوي على روابط الصفحات في المواقع التي نُريد استخراج عناوين البريد الإلكتروني منها:

pages = [
    'http://www.pmdp.ps/ListOfBusiness',
    'http://www.pmdp.ps/roster',
]

نُعرف قائمة فارغة باسم emails_list سنعمل على حفظ عنواين البريد الالكتروني فيها، ثم نُعرف نمط البريد الالكتروني باسم email_rgx:

emails_list = []

email_rgx = r"[a-z0-9\.\-+_][email protected][a-z0-9\.\-+_]+\.com"

الجزء الرئيسي في المثال هو تعريف حلقة تكرار for loop تعمل على المرور على الصفحات الموجودة في قائمة pages ثم نٌرسل طلب request لكل صفحة ونُسند الرد في مُتغير باسم response. باستخدام الخاصية text الموجود في الرد يُمكننا استدعاء دالة findall الموجودة في مكتبة التعابير النمطية، وستعمل الدالة على البحث في الرد (والذي سيكون عبارة عن كود Html للصفحات) على كُل مجموعة كلمات تتوافق مع نمط البريد الإلكتروني، ثم نحفظ النتيجة في قائمة البريد الالكتروني emails_list ثم نطبعها:

for page in pages:
    response = requests.get(page)
    emails = re.findall(email_rgx, response.text)
    emails_list.extend(emails)

print(emails_list)

والنتيجة هي مجموعة البريد الالكتروني الموجودة في الصفحات التي بحثنا فيها:

[
' [email protected] ', 
' [email protected] ',
' [email protected] ',
' [email protected] ', 
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ', 
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ', 
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
' [email protected] ',
]

الشيفرة البرمجية الكاملة للمثال:

import requests
import re

pages = [
    'http://www.pmdp.ps/ListOfBusiness',
    'http://www.pmdp.ps/roster',
]

emails_list = []

email_rgx = r"[a-z0-9\.\-+_][email protected][a-z0-9\.\-+_]+\.com"

for page in pages:
    response = requests.get(page)
    emails = re.findall(email_rgx, response.text)
    emails_list.extend(emails)

print(emails_list)

إلى هنا نكون قد انتهينا من شرح مكتبة requests بشكل مبسط وسهل، وقدمنا مثالًا عمليًا لاستخدامها. ونرجو أن نكون قد أفدناكم، وسنكون سُعداء بمشاركتكم معنا بالتعليق والإعجاب والمشاركة في حسابات بايثونات على وسائل الاتصال الاجتماعي.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *