١. HTTP — كيف يتحدث الإنترنت
كل مرة تزور موقعاً، برنامجك يرسل Request للخادم، والخادم يرد بـ Response. هذا هو HTTP.
Client
GET /data
Server
200 OK + JSON
يعالج البيانات
أنواع طلبات HTTP
GET — اجلب بيانات ·
POST — أرسل بيانات جديدة ·
PUT — حدّث بيانات كاملة ·
PATCH — حدّث جزئياً ·
DELETE — احذف
رموز الحالة الشائعة
200OK — طلب ناجح
201Created — تم الإنشاء
301Moved — تحويل دائم
400Bad Request — طلب خاطئ
401Unauthorized — غير مصرح
404Not Found — غير موجود
500Server Error — خطأ الخادم
429Too Many Requests
٢. مكتبة requests — أسهل طريقة لجلب البيانات
ثبّت المكتبة: pip install requests
import requests
# ===== GET — جلب بيانات =====
resp = requests.get("https://httpbin.org/get")
print(resp.status_code) # 200
print(resp.headers) # معلومات الاستجابة
print(resp.text) # المحتوى كنص
print(resp.json()) # تحويل JSON تلقائياً
# ===== GET مع معاملات =====
params = {"q": "python tutorial", "page": 1, "limit": 10}
resp = requests.get("https://httpbin.org/get", params=params)
# URL: https://httpbin.org/get?q=python+tutorial&page=1&limit=10
# ===== POST — إرسال بيانات =====
data = {"username": "khalil", "password": "secret"}
resp = requests.post("https://httpbin.org/post", json=data)
print(resp.json()["json"]) # يُرجع ما أرسلناه
# ===== Headers مخصصة =====
headers = {
"User-Agent" : "MyApp/1.0",
"Accept-Language": "ar,en",
"Content-Type" : "application/json",
}
resp = requests.get("https://httpbin.org/headers", headers=headers)
# ===== Timeout — لا تنتظر إلى الأبد =====
resp = requests.get("https://httpbin.org/delay/1", timeout=5)
# إذا لم يرد الخادم في 5 ثوانٍ → Timeout error
# ===== Session — إعادة استخدام الاتصال =====
session = requests.Session()
session.headers["User-Agent"] = "MyBot/1.0"
r1 = session.get("https://httpbin.org/get")
r2 = session.get("https://httpbin.org/get")
# نفس الـ headers لكلا الطلبين
🧠 سؤال سريع
متى يجب استخدام POST بدل GET؟
GET لجلب البيانات، POST لإرسال بيانات جديدة. GET يضع المعاملات في الـ URL، POST يضعها في الـ body.GET لجلب البيانات دون تعديل. POST لإرسال بيانات جديدة للخادم كتسجيل مستخدم أو نشر منشور.٣. التعامل مع REST APIs الحقيقية
import requests
# ١. API مجانية — JSONPlaceholder (للتجربة)
BASE = "https://jsonplaceholder.typicode.com"
# جلب كل المنشورات
posts = requests.get(f"{BASE}/posts").json()
print(f"عدد المنشورات: {len(posts)}")
print(f"أول منشور: {posts[0]['title']}")
# جلب منشور محدد
post = requests.get(f"{BASE}/posts/1").json()
print(f"العنوان: {post['title']}")
# إنشاء منشور جديد
new_post = {
"title" : "تعلم بايثون",
"body" : "بايثون لغة رائعة للمبتدئين",
"userId" : 1
}
resp = requests.post(f"{BASE}/posts", json=new_post)
print(f"تم الإنشاء — ID: {resp.json()['id']}, Status: {resp.status_code}")
# ٢. API الطقس (تحتاج مفتاح مجاني من openweathermap.org)
def get_weather(city: str, api_key: str) -> dict:
url = "https://api.openweathermap.org/data/2.5/weather"
params = {"q": city, "appid": api_key, "units": "metric", "lang": "ar"}
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status() # يرمي خطأ إذا 4xx أو 5xx
data = resp.json()
return {
"مدينة" : data["name"],
"حرارة" : f"{data['main']['temp']}°C",
"وصف" : data["weather"][0]["description"],
"رطوبة" : f"{data['main']['humidity']}%",
"سرعة الرياح": f"{data['wind']['speed']} m/s",
}
# weather = get_weather("Marrakech", "YOUR_API_KEY")
# for k, v in weather.items(): print(f"{k}: {v}")
API مجانية للتجربة — لا تحتاج مفتاح
import requests
# ١. معلومات عن دولة
r = requests.get("https://restcountries.com/v3.1/name/morocco")
data = r.json()[0]
print(f"الدولة : {data['name']['common']}")
print(f"العاصمة : {data['capital'][0]}")
print(f"السكان : {data['population']:,}")
print(f"العملة : {list(data['currencies'].keys())[0]}")
# ٢. ترجمة نص (LibreTranslate — مجاني)
resp = requests.post(
"https://libretranslate.de/translate",
json={"q": "Hello World", "source": "en", "target": "ar"},
timeout=10
)
if resp.ok:
print(resp.json()["translatedText"]) # مرحباً بالعالم
# ٣. معلومات IP
ip_data = requests.get("https://ipapi.co/json/").json()
print(f"IP: {ip_data.get('ip')} | البلد: {ip_data.get('country_name')}")
٤. المصادقة — API Keys و Tokens
import requests
import os
# ===== ١. API Key في Header (الأكثر شيوعاً) =====
API_KEY = os.getenv("MY_API_KEY") # من متغيرات البيئة
headers = {"Authorization": f"Bearer {API_KEY}"}
resp = requests.get("https://api.example.com/data", headers=headers)
# ===== ٢. API Key في Params =====
params = {"api_key": API_KEY, "city": "Marrakech"}
resp = requests.get("https://api.weather.com/current", params=params)
# ===== ٣. Basic Auth =====
resp = requests.get(
"https://httpbin.org/basic-auth/user/pass",
auth=("user", "pass")
)
print(resp.json())
# ===== حفظ API Key بأمان في .env =====
# ١. أنشئ ملف .env:
# MY_API_KEY=abc123secretkey
# ٢. ثبّت python-dotenv: pip install python-dotenv
from dotenv import load_dotenv
load_dotenv()
key = os.getenv("MY_API_KEY")
# لا تكتب المفتاح مباشرة في الكود — خطر أمني!
.env مع إضافته لـ .gitignore.٥. معالجة أخطاء الشبكة بشكل صحيح
import requests
from requests.exceptions import (
Timeout, ConnectionError, HTTPError, RequestException
)
def safe_request(url: str, **kwargs) -> dict | None:
try:
resp = requests.get(url, timeout=10, **kwargs)
resp.raise_for_status() # يرمي HTTPError للـ 4xx/5xx
return resp.json()
except Timeout:
print("⏱️ الخادم لم يرد خلال 10 ثوانٍ")
except ConnectionError:
print("🔌 لا يوجد اتصال بالإنترنت")
except HTTPError as e:
print(f"❌ خطأ HTTP: {e.response.status_code}")
if e.response.status_code == 401:
print(" المفتاح منتهي أو غير صحيح")
elif e.response.status_code == 429:
print(" تجاوزت حد الطلبات — انتظر قليلاً")
except RequestException as e:
print(f"❓ خطأ غير متوقع: {e}")
return None
# Retry تلقائي مع backoff
import time
def request_with_retry(url: str, retries=3, backoff=2):
for attempt in range(retries):
result = safe_request(url)
if result:
return result
wait = backoff ** attempt
print(f"محاولة {attempt+1}/{retries} — انتظر {wait}s")
time.sleep(wait)
return None
٦. Web Scraping — استخراج بيانات المواقع
عندما لا يوجد API، يمكنك قراءة صفحة HTML وتحليلها. مع الالتزام دائماً بـ robots.txt وشروط الاستخدام.
ثبّت: pip install requests beautifulsoup4 lxml
import requests
# جلب صفحة HTML
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64)"}
resp = requests.get("https://books.toscrape.com",
headers=headers, timeout=10)
print(f"Status : {resp.status_code}")
print(f"Encoding: {resp.encoding}")
print(resp.text[:300]) # أول 300 حرف من HTML
/robots.txt · لا تضغط على الخادم (أضف sleep بين الطلبات) · لا تستخدم البيانات لأغراض تجارية دون إذن · بعض المواقع يستخدم JavaScript لتحميل البيانات — حينها تحتاج Selenium.٧. BeautifulSoup — تحليل HTML
import requests
from bs4 import BeautifulSoup
headers = {"User-Agent": "Mozilla/5.0"}
resp = requests.get("https://books.toscrape.com", headers=headers)
soup = BeautifulSoup(resp.text, "lxml")
# ===== الوصول للعناصر =====
print(soup.title.text) # عنوان الصفحة
# عنصر واحد بـ CSS selector
first_h1 = soup.select_one("h1")
print(first_h1.text.strip())
# كل الروابط
links = soup.select("a[href]")
for a in links[:5]:
print(a["href"], "|", a.text.strip())
# ===== مثال حقيقي: scraping كتالوج كتب =====
books = []
for article in soup.select("article.product_pod"):
books.append({
"title" : article.select_one("h3 a")["title"],
"price" : article.select_one(".price_color").text.strip(),
"rating": article.select_one("p.star-rating")["class"][1],
})
print(f"وُجد {len(books)} كتاب:")
for b in books[:5]:
print(f" 📚 {b['title'][:40]:<40} {b['price']:>10} ⭐{b['rating']}")
# حفظ النتائج
import json
with open("books.json", "w", encoding="utf-8") as f:
json.dump(books, f, ensure_ascii=False, indent=2)
print("✅ تم الحفظ في books.json")
🧠 سؤال سريع
ما الفرق بين soup.find() و soup.select()؟
select("h3 a") يفهم CSS selectors كاملاً — أقوى بكثير من find("h3").select() يستخدم CSS selectors مثل "article.product h3 a"، بينما find() يستخدم اسم الوسم فقط مع attrs اختياري.📝 تمارين تطبيقية
تمرين ١ — جلب وعرض أسعار العملات
استخدم API المجانية https://open.er-api.com/v6/latest/USD لجلب أسعار صرف الدرهم المغربي والريال السعودي والجنيه المصري مقابل الدولار.
import requests
def get_exchange_rates():
url = "https://open.er-api.com/v6/latest/USD"
resp = requests.get(url, timeout=10)
resp.raise_for_status()
rates = resp.json()["rates"]
wanted = {
"MAD": "🇲🇦 درهم مغربي",
"SAR": "🇸🇦 ريال سعودي",
"EGP": "🇪🇬 جنيه مصري",
"EUR": "🇪🇺 يورو",
}
print("💵 1 دولار أمريكي =")
for code, name in wanted.items():
print(f" {name:20} {rates.get(code, 'N/A'):.4f}")
get_exchange_rates()
تمرين ٢ — Scraper متعدد الصفحات
اكتب scraper يجلب الكتب من أول 3 صفحات من books.toscrape.com ويحفظها في CSV.
import requests, csv, time
from bs4 import BeautifulSoup
BASE = "https://books.toscrape.com/catalogue"
headers = {"User-Agent": "Mozilla/5.0"}
all_books = []
for page in range(1, 4):
url = f"{BASE}/page-{page}.html"
resp = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(resp.text, "lxml")
for a in soup.select("article.product_pod"):
all_books.append({
"title" : a.select_one("h3 a")["title"],
"price" : a.select_one(".price_color").text.strip(),
"rating": a.select_one("p.star-rating")["class"][1],
"page" : page
})
print(f"✅ صفحة {page} — {len(all_books)} كتاب حتى الآن")
time.sleep(1) # نحترم الخادم
# حفظ CSV
with open("books.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["title","price","rating","page"])
writer.writeheader()
writer.writerows(all_books)
print(f"💾 تم الحفظ: {len(all_books)} كتاب")
🚀 المشروع — لوحة تحكم بيانات حية
import requests, json, time
from datetime import datetime
class LiveDashboard:
"""لوحة بيانات حية تجمع من عدة APIs"""
def __init__(self):
self.session = requests.Session()
self.session.headers["User-Agent"] = "Dashboard/1.0"
self.data = {}
def _get(self, url, **kw):
try:
r = self.session.get(url, timeout=8, **kw)
r.raise_for_status()
return r.json()
except Exception as e:
print(f" ⚠️ {url.split('/')[2]}: {e}")
return None
def fetch_country(self, name="Morocco"):
d = self._get(f"https://restcountries.com/v3.1/name/{name}")
if d:
c = d[0]
self.data["country"] = {
"name" : c["name"]["common"],
"pop" : f"{c['population']:,}",
"capital": c["capital"][0],
"region" : c["region"],
}
def fetch_rates(self):
d = self._get("https://open.er-api.com/v6/latest/USD")
if d:
r = d["rates"]
self.data["rates"] = {
"MAD": r.get("MAD"), "EUR": r.get("EUR"),
"SAR": r.get("SAR"), "GBP": r.get("GBP"),
}
def display(self):
print("\n" + "═"*45)
print(f" 📊 LIVE DASHBOARD — {datetime.now():%H:%M:%S}")
print("═"*45)
if c := self.data.get("country"):
print(f" 🌍 {c['name']} | {c['capital']} | {c['region']}")
print(f" السكان: {c['pop']}")
if r := self.data.get("rates"):
print(f"\n 💱 أسعار الصرف (1 USD =)")
for cur, val in r.items():
print(f" {cur}: {val:.4f}")
def run(self):
print("🔄 جلب البيانات...")
self.fetch_country()
self.fetch_rates()
self.display()
LiveDashboard().run()
🎉 أتقنت APIs و Web Scraping!
الآن تعرف كيف تتصل بأي API في العالم وتجلب البيانات وتحللها وتحفظها. هذه المهارة تفتح لك أبواباً لا حد لها.
← العودة للبداية