شرح مكتبة requests في بايثون | ما هو ال HTTP | ما هو ال Web Scraping


مكتبة ال requests واحدة من اكثر المكتبات تحميلا بلغة بايثون. هذا لانها تجعل عملية ارسال طلبات HTTP  سهلة للغاية مقارنة بلغات برمجة اخرى. تسمح لنا طلبات ال HTTP بالتفاعل مع المواقع الالكترونية واجراء عمليات مثل تحميل معلومات من الموقع كالصور والمعلومات النصية وحتى الكود الخاص بالموقع وارسال معلومات الى الموقع وكثير من المهام الاخرى. نقوم في هذا المقال والفيديو بشرح اساسيات ال HTTP  وال web scraping  اولا ثم نقوم بشرح مكتبة ال requests بالتفصيل

بدايةً لابد من فهم بعض المصطلحات الاساسية:

 مصطلح web scraping:

عملية ال web scraping هي عملية الحصول على معلومات سواء صور، أفلام، معلومات نصية او غير ذلك من الانترنت باستخدام البرمجة. هناك طريقتين رئيسيتين للقيام بهذه المهمة. 

الطريقة الاولى هي باستخدام ال API (Application Programming Interface) وهي خدمة مجانية او مدفوعة توفرها بعض المواقع الالكترونية التي تحتوي على كمية بيانات ضخمة والتي من خلالها نستطيع التفاعل مع الموقع بالحصول على معلومات معينة او ارسال معلومات الى الموقع. فمثلا يوفر موقع Facebook ال API الخاص به والذي يسمح لمطوري البرامج والمواقع بربط برامجهم بال Facebook لتسهيل عملية تسجيل الدخول. تستخدم مكتبة ال requests في هذا الطريقة.
الطريقة الثانية وتدعى web parsing تسمح لك بالحصول على المعلومات بالقيام بتحليل الكود الخاص بالموقع وغالبا ما يكون كود HTML  او جافا سكريبت. باستخدام هذه الطريقة يقوم المبرمج بسحب الكود الخاص بالموقع وتحليله لتحديد اماكن المعلومات التي يرغب بالحصول عليها ثم يقوم بكتابة برنامج لاستخراج هذا المعلومات. تستخدم في هذه الطريقة مكتبات اخرى مثل Beautiful Soup او requests-html.

مصطلح ال HTTP:

هي اختصار ل Hypertext Transfer Protocol وهو ما يستخدمه الانترنت ليعمل بالطريقة التي نراها اليوم. يوفر هذا البروتوكل طريقة للتواصل بين الخادم والعميل. ففي كل مرة تقوم بفتح المتصفح الخاص بك لفتح موقع فأنت تقوم باستخدام ال HTTP. فعندما تقوم بكتابة اسم الموقع الذي ترغب بقتحه من المتصفح، فأنت تقوم بارسال طلب (HTTP Request) ثم يقوم الخادم بالرد على طلبك ويرسل لك معلومات تتضمن نتائج طلبك (HTTP Response). تدعى هذه العملية HTTP request/response cycle. انت لا تلاحظ كل هذا لان المتصفح يقوم بهذه العمليات لك. ولكن عند استخدام البرمجة لهذا الغرض يجب عليك فهم هذه العملية لتكتب برامج ذي قيمة. 

هناك اربعة أنواع رئيسية من طلبات ال HTTP  تسمى HTTP Methods:

النوع الأول : GET

تسمح لنا هذه الطريقة بالحصول على معلومات من الموقع او الخادم (Server) الخاص بالموقع. فعند قيامك بفتح موقع معين فأنت تقوم بارسال GET request

النوع الثاني: POST

تسمح لنا هذه الطريقة بانشاء معلومات على الخادم الخاص بالموقع كتحميل صورة جديدة على صفحتك الخاصة بموقع ال Facebbok

النوع الثالث: PUT

تسمح لنا هذه الطريقة بتحديث المعلومات على الخادم الخاص بالموقع كتعديل مقال لك على ال Facebook

النوع الرابع: DELETE

تسمح لنا هذه الطريقة بحذف معلومات من الخادم الخاص بالموقع كحذف صورة لك من موقع ال Facebook

يحتوي كل من الطلب (HTTP Request) و الرد (HTTP Response) على قسمين رئيسيين وهما الرأس والجسد (Header and Body)

يحتوي الجسد (Body) على المعلومات الرئيسية. فمثلا عند قيامك بتحميل صورة الى موقع ال Facebook  تكون الصورة في جسد الطلب. وعندما تقوم بفتح موقع يكون كود ال HTML او الجافاسكريبت  في جسد الرد. 

يحتوي الرأس (Header) على معلومات اضافية تساعد الخادم والعميل على فهم الطلب والرد. وتتكون من 3 اقسام

1- معلومات عامة General:

Request URL

Request Method

Status Code

Remote Address

2- معلومات خاصة بالرد ()

Server

Content Type

  • text/html 
  • text/css
  • img/png, jpg
  • application/json

Date

3- معلومات خاصة بالطلب (Request)

Content-Type

text/html

text/css

img/png, jpg

application/json

Content-Length

Authorization

ما هي ال HTTP Status Code

1XX Information معلومات

تم استلام الطلب

2XX: Sucess نجاح

200 OK: تم تنفيذ طلبك بنجاح

3XX: Redirects اعادة توجيه

تم نقل الموقع الى عنوان اخر 301

4XX: Client Error مشكلة في الطلب

طلبك لا يحتوي على المعلومات الضرورية لاتمام الطلب 400  

5XX: Server Error مشكلة في الخادم

لم يستطع الخام تنفيذ طلبك 500

لنبدأ بالتعرف على مكتبة requests والتي تسمح لنا بالقيام بهذه المهام بكل سهولة

مكتبة ال requests ليست من ضمن المكتبات الاساسية في بايثون وعليك تنزيلها باستخدام ال pip قبل استخدامها.

لنقم الان بفتح صفحة لغة البرمجة بايثون في موقع wikipedia. سنقوم باستخدام ال GET Method

import requests


r = requests.get("https://en.wikipedia.org/wiki/Python_(programming_language)")

 

باستخدام هذا الكود قمنا بارسال طلب (Request) وخزنا الرد (Response) بالمتغير “r”. لنقم بطباعة المتغير “r”

print(r)

نلاحظ انه طبع لنا response object  مع status code 200 والذي يعبر عن نجاح تنفيذ الطلب. 

يمكننا الحصول على ال status code  فقط كما يلي:

print(r.status_code)

يمكننا الحصول على كل الصفات (attributes) والطرق (methods) التي يوفرها هذا الرد كما يلي:

print(dir(r))

يمكننا معرفة تفاصيل اكثر عن كل  الصفات (attributes) والطرق (methods) التي يوفرها هذا الرد كما يلي

print(help(r))

لنقم بطباعة ال r.text

print(r.text.encode("UTF-8"))

 

سنلاحظ انه طبع لنا ال HTML كود لهذه الصفحة. هذا هو الكود الذي يقوم المتصفح بترجمته الى نصوص وصور وعرض الموقع لنا. اذا اردت الحصول على معلومات من هذا الكود ستسخدم مكتبات اخرى مثل requests-html  او Beautiful Soup لتقوم بعملية ال parsing التي تحدثنا عنها سابقا في هذا المقال. 

لنقم الان باخذ عنوان صورة شعار بايثون الموجود على الصفحة بالضغط بزر الماوس اليمين على الصورة ثم الضغط على copy image address ثم ننفذ الكود الاتي:

r = requests.get("https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Python_logo_and_wordmark.svg/250px-Python_logo_and_wordmark.svg.png")
print(r.content)

لاحظ انه قام بطباعة ال byte code لهذه الصورة. لحفظ هذه الصورة سنقوم باستخدام الكود التالي. لدينا فيديو يشرح كيف تتعامل مع الملفات في بايثون:

r = requests.get(
    "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Python_logo_and_wordmark.svg/250px-Python_logo_and_wordmark.svg.png"
)
with open("logo.png", "wb") as f:
    f.write(r.content)

ستجد الصورة محفوظة في المجلد الذي يحتوي على برنامج البايثون الذي تقوم بالعمل عليه.

لنقم الان بطباعة رأس (headers) الرد:

r = requests.get(    "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Python_logo_and_wordmark.svg/250px-Python_logo_and_wordmark.svg.png")  
print(r.headers)

سنستخدم من الان وصاعدا في هذا المقال موقع http://httpbin.org للتعرف اكثر على مكتبة requests. هذا الموقع تم انشاؤه من قبل الناشر لمكتبة requests خصيصا لتعليم مكتبة requests.

اذا ما نظرنا الى اي عنوان سنجد انه يتكون من قسمين رئيسين. لنأخذ هذا العنوان من موقع امازون على سبيل المثال:

https://www.amazon.com/b?node=16225009011&pf_rd_r=Z4T62YZT3YTZ8JA605P3&pf_rd_p=5232c45b-5929-4ff0-8eae-5f67afd5c3dc&pd_rd_r=f2c201bf-4db8-4c9a-9d18-2c37ffb21ca2&pd_rd_w=wf4WS&pd_rd_wg=WRfTh&ref_=pd_gw_unk

 بالنظر الى هذا العنوان نجد انه يتكون من العنوان الرئيسي للموقع ويدعى base URL :

https://www.amazon.com

ونجد ايضا قسم آخر يبدأ من بعد علامة الاستفهام “؟” يدعى (payload) يحتوي على معلومات اخرى تدعى العوامل (query parameters):

فمثلا نجد العامل node  وقيمته 1622500901 ونجد العامل pf_rd_r وقيمته Z4T62YZT3YTZ8JA605P3 وهكذا. نلاحظ ايضا انه يوجد علامة “&” بين كل عامل والاخر.

تختلف هذه العوامل من موقع لاخر. 

لنعود الان الى موقع httpbin.org. كما قلنا سابقا فهذا الموقع لتعليم requests وبالتالي سوف يقبل منك اي عوامل ستقوم بارسالها. لنجرب هذا الكود

r = requests.get("http://httpbin.org/get?country=Palestine&capital=Jerusalem")

print(r.status_code)


لاحظ اننا قمنا بكتابة العوامل (query parameters) ملاصقة للعنوان الرئيسي (base URL). على الرغم من انه يمكنك استخدام هذه الطريقة، الا انها قد تعرضك للخطأ بتشكيل العنوان. توفر لنا مكتبة requests طريقة افضل لارسال هذه العوامل و هي باستخدام قاموس كما يلي:

 

payload = {"country": "Palestine", "capital": "Jerusalem"}

r = requests.get(url="http://httpbin.org/get", params=payload)

print(r.status_code)

 

نلاحظ ان الرد يحتوي على نص json وبالتالي كان الممكن ان نستخدم r.json() لطباعته ك json. يحتوي الرد على ال query parameters كما يحتوي على headers والرابط الذي كونته لنا مكتبة requests من ال base url  وال query parameters.

لنقم الان بطباعة محتوى الرد باستخدام r.text

payload = {"country": "Palestine", "capital": "Jerusalem"}

r = requests.get(url="http://httpbin.org/get", params=payload)

print(r.text)

لنقم باستخدام r.json() للولوج الى ال headers وحفظها في متغير منفصل

payload = {"country": "Palestine", "capital": "Jerusalem"}

r = requests.get(url="http://httpbin.org/get", params=payload)

json = r.json()

headers = json["headers"]

print(headers)

 

لطباعة الرابط فقط يمكن استخدام r.url

payload = {"country": "Palestine", "capital": "Jerusalem"}

r = requests.get(url="http://httpbin.org/get", params=payload)

print(r.url)

لاحظ ان الرابط هو تماما الرابط الذي استخدمناه اولا.

بطريقة مشابهة جدا يمكننا ارسال طلب POST كما يلي:
 

payload = {"fname": "Omar", "lname": "Ghoneim"}

r = requests.post(url="http://httpbin.org/post", data=payload)

json = r.json()

print(json)

يمكننا ايضا استخدام نفس الطريقة لارسال طلب PUT

لنرى الأن كيف يمكننا فتح صفحة ما تتطلب اسم مستخدم وكلمة سر (basic authentication). يمكننا انشاء صفحة تتطلب اسم مستخدم وكلمة سر من موقع httpbin.org باتباع الرابط التالي

http://httpbin.org/basic-auth/username/password

لنضع اسم المستخدم “omar” وكلمة السر “1234”. فيصبح الرابط:

http://httpbin.org/basic-auth/omar/1234

والان لفتح الصفحة وارسال اسم المستخدم وكلمة السر يمكننا استخدام الكود التالي:

r = requests.get(url="http://httpbin.org/basic-auth/omar/1234", auth=("omar", "1234"))

print(r.status_code)

print(r.text)

لنجرب ونضع اسم مستخدم خاطيء

r = requests.get(url="http://httpbin.org/basic-auth/omar/1234", auth=("ahmad", "1234"))

print(r.status_code)

print(r.text)

سنلاحظ ان ال status code هو 401 والذي يعبر عن دخول غير مرخص (unauthorized access)

ماذا لو لم يستجيب الموقع لطلبنا لوقت طويل؟ عادة اذا لم يرد الموقع على الطلب سيبقى الطلب معلقا الى مالانهاية. لتجنب هذه المشكلة يمكننا استخدام المتغير timeout الذي توفره لنا مكتبة requests. يمكننا انشاء صفحة مع تأخير مبرمج من موقع httpbin.org باتباع الرابط التالي:

http://httpbin.org/delay/{delay}

يمكننا وضع وقت التأخير بالثواني بتعويض عدد الثواني مكان {delay} في الرابط السابق. فمثلا اذا اردنا انشاء صفحة تتأخر بالرد لمدة 3 ثواني سيصبح الرابط كما يلي:

http://httpbin.org/delay/3

يمكننا الان استخدام هذا الرابط كما يلي:

r = requests.get(url="http://httpbin.org/delay/3", timeout=4)

print(r.status_code)

print(r.text)


لاحظ ان ال timeout الذي استخدمناه هو 4 ثواني وهو اقل من المدة التي برمجناها كوقت تاخر في الموقع. وبالتالي فان الموقع رد على طلبنا بعد 3 ثواني وطبع لنا البرنامج ال status code وال text

لنضع ال timeout اقل من 3 ثواني ونعيد التجربة

r = requests.get(url="http://httpbin.org/delay/3", timeout=2)

print(r.status_code)

print(r.text)

لاحظ ان البرنامج انتظر ثانيتين وعندما لم يحصل على رد بعد الثانيتين اظهر لنا requests.exceptions.ReadTimeout

يمكننا التقاط هذا ال exception باستخدام try/except block  كما يلي:

try:

    r = requests.get(url="http://httpbin.org/delay/3", timeout=2)

    print("Success")

except requests.exceptions.ReadTimeout:

    print("Timeout")


لاحظ انه بعد ان مرت الثانيتين ولم يرد الموقع على طلبنا قام البرنامج بطباعة (Timeout)