مكتبة ال 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
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 :
ونجد ايضا قسم آخر يبدأ من بعد علامة الاستفهام “؟” يدعى (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 ثواني سيصبح الرابط كما يلي:
يمكننا الان استخدام هذا الرابط كما يلي:
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)