إذا كان سكربت Python الخاص بك يتلقى خطأ 403، CAPTCHA أو حظر IP — فهذا يعني أن الموقع المستهدف قد لاحظك بالفعل. توصيل البروكسي بمكتبة requests يحل هذه المشكلة: تقوم بتغيير عنوان IP، وتجاوز القيود الجغرافية وتوزيع الحمل بين عدة عناوين. في هذا الدليل — كل شيء من الاتصال الأساسي إلى التدوير المتقدم مع أمثلة حقيقية من الشيفرة.
لماذا تحتاج إلى بروكسي في سكربتات Python
معظم المواقع و API تتعقب عناوين IP للطلبات الواردة. إذا كان عنوان واحد يقوم بأكثر من 100 طلب في الدقيقة — يتم حظره. هذه هي الحماية القياسية ضد الروبوتات، التي تستخدمها Wildberries، Ozon، Avito، Google، Instagram ومئات من المنصات الأخرى. يسمح البروكسي بتوجيه الطلب عبر خادم وسيط مع عنوان IP مختلف، مما يجعلك غير مرئي لأنظمة الحماية.
إليك المهام الرئيسية التي يكون فيها البروكسي في Python ضروريًا بشكل حاسم:
- جمع البيانات من الأسواق — جمع الأسعار من Wildberries، Ozon، Yandex.Market بدون حظر IP
- مراقبة المنافسين — طلبات منتظمة لمواقع المنافسين كل 5-15 دقيقة
- العمل مع API مع حدود — توزيع الطلبات بين عدة IP، لتجنب تجاوز حد المعدل
- اختبار الموقع الجغرافي — التحقق من كيفية ظهور الموقع من دول ومناطق مختلفة
- أتمتة النماذج والتسجيلات — إنشاء حسابات أو ملء النماذج من عناوين IP مختلفة
- مراقبة SEO — سحب المراكز من مناطق مختلفة في روسيا ودول أخرى
بدون البروكسي، حتى أفضل برنامج لجمع البيانات سيواجه الحظر بعد بضع ساعات من العمل. مع تدوير IP بشكل صحيح، يعمل نفس السكربت لأسابيع دون توقف.
الإعداد الأساسي للبروكسي في requests
تدعم مكتبة requests البروكسي بشكل أصلي — لا حاجة لأي حزم إضافية. يتم تمرير البروكسي عبر قاموس proxies في معلمات الطلب.
أبسط مثال — بروكسي HTTP لطلب واحد:
import requests
proxies = {
"http": "http://123.45.67.89:8080",
"https": "http://123.45.67.89:8080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
# {'origin': '123.45.67.89'}
لاحظ: في قاموس proxies يجب تحديد كلا المفتاحين — http و https. إذا تم تحديد واحد فقط، ستذهب الطلبات عبر البروتوكول الثاني مباشرة بدون بروكسي. هذه خطأ شائع بين المبتدئين، مما يؤدي إلى تسرب IP الحقيقي.
للتأكد من أن البروكسي يعمل، استخدم خدمة httpbin.org/ip — فهي تعيد عنوان IP الذي جاء منه الطلب. إذا كنت ترى في الرد IP خادم البروكسي وليس IP الخاص بك — كل شيء تم إعداده بشكل صحيح.
بروكسي HTTP، HTTPS و SOCKS5: الاختلافات والأمثلة من الشيفرة
توجد أنواع مختلفة من البروكسي، وكل نوع مناسب لمهامه الخاصة. في سياق Python requests، من المهم فهم الفرق بين البروتوكولات الثلاثة الرئيسية:
| النوع | البروتوكول في URL | السرعة | دعم UDP | أفضل سيناريو |
|---|---|---|---|---|
| HTTP | http:// |
عالية | لا | جمع البيانات من مواقع HTTP |
| HTTPS | https:// |
عالية | لا | جمع البيانات من المواقع المحمية |
| SOCKS5 | socks5:// |
متوسطة | نعم | خصوصية كاملة، أي بروتوكولات |
للعمل مع SOCKS5 في Python، تحتاج إلى تثبيت حزمة إضافية:
pip install requests[socks] # أو بشكل منفصل: pip install PySocks
بعد التثبيت، يبدو الاتصال ببروكسي SOCKS5 كالتالي:
import requests
# بروكسي SOCKS5
proxies = {
"http": "socks5://123.45.67.89:1080",
"https": "socks5://123.45.67.89:1080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
SOCKS5 هو البروتوكول المفضل للمهام التي تتطلب الخصوصية. على عكس بروكسي HTTP، لا يضيف SOCKS5 رؤوس X-Forwarded-For، التي يمكن أن تكشف عن IP الحقيقي الخاص بك.
بروكسي مع مصادقة اسم المستخدم وكلمة المرور
تستخدم معظم خدمات البروكسي المدفوعة المصادقة عبر اسم المستخدم وكلمة المرور. هذه ممارسة قياسية — بدون المصادقة، لن يسمح البروكسي بطلبك. في مكتبة requests، يتم تمرير بيانات المصادقة مباشرة في URL البروكسي.
import requests # التنسيق: البروتوكول://اسم المستخدم:كلمة المرور@المضيف:المنفذ proxy_url = "http://myuser:[email protected]:8080" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://httpbin.org/ip", proxies=proxies) print(response.status_code) print(response.json())
إذا كانت كلمة المرور أو اسم المستخدم يحتويان على رموز خاصة (مثل @، #، %)، يجب ترميزها في URL. للقيام بذلك، استخدم وحدة urllib.parse:
import requests
from urllib.parse import quote
username = "myuser"
password = "p@ss#word!" # رموز خاصة
# ترميز اسم المستخدم وكلمة المرور
encoded_user = quote(username, safe="")
encoded_pass = quote(password, safe="")
proxy_url = f"http://{encoded_user}:{encoded_pass}@123.45.67.89:8080"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
💡 نصيحة للأمان
لا تقم أبدًا بتشفير اسم المستخدم وكلمة المرور مباشرة في كود السكربت. استخدم متغيرات البيئة أو ملف .env مع مكتبة python-dotenv. سيساعدك ذلك في تجنب تسرب بيانات الاعتماد عند نشر الكود على GitHub.
تدوير البروكسي: تغيير IP تلقائي لجمع البيانات
بروكسي واحد — لا يزال عنوان IP واحد يمكن حظره. الحماية الحقيقية من الحظر هي التدوير: كل طلب (أو كل N طلبات) يخرج من IP جديد. أدناه — بعض الطرق لتنفيذ التدوير.
الطريقة 1: اختيار عشوائي من القائمة
import requests
import random
# قائمة البروكسي
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
def get_random_proxy():
proxy = random.choice(proxy_list)
return {"http": proxy, "https": proxy}
# جمع البيانات من 10 صفحات مع تدوير IP
urls = [f"https://example.com/page/{i}" for i in range(1, 11)]
for url in urls:
proxies = get_random_proxy()
try:
response = requests.get(url, proxies=proxies, timeout=10)
print(f"URL: {url} | IP: {proxies['http'].split('@')[1]} | الحالة: {response.status_code}")
except requests.RequestException as e:
print(f"خطأ: {e}")
الطريقة 2: تدوير دائري عبر itertools
import requests
import itertools
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
# إنشاء حلقة لانهائية عبر قائمة البروكسي
proxy_cycle = itertools.cycle(proxy_list)
def get_next_proxy():
proxy = next(proxy_cycle)
return {"http": proxy, "https": proxy}
# كل طلب يستخدم البروكسي التالي في الدورة
for i in range(9):
proxies = get_next_proxy()
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(f"الطلب {i+1}: {response.json()['origin']}")
للمهام الصناعية مع آلاف الطلبات في الساعة، يوصى باستخدام بروكسي سكنية مع تدوير تلقائي مدمج — يقوم المزود بتغيير IP لكل طلب عبر نقطة نهاية واحدة، ولا تحتاج إلى إدارة قائمة العناوين يدويًا.
بروكسي عبر الجلسة: اتصالات دائمة وملفات تعريف الارتباط
عندما تحتاج إلى إجراء عدة طلبات ضمن جلسة واحدة (مثل تسجيل الدخول ثم إجراء الطلبات)، استخدم كائن requests.Session(). يحتفظ بملفات تعريف الارتباط، الرؤوس وإعدادات البروكسي بين الطلبات — لا حاجة لتمرير البروكسي في كل استدعاء بشكل منفصل.
import requests
# إنشاء جلسة مع البروكسي
session = requests.Session()
session.proxies = {
"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080",
}
# إضافة الرؤوس لمحاكاة المتصفح
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "ru-RU,ru;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
})
# الخطوة 1: المصادقة
login_data = {"username": "myuser", "password": "mypass"}
session.post("https://example.com/login", data=login_data)
# الخطوة 2: الطلبات مع ملفات تعريف الارتباط وعبر البروكسي
response = session.get("https://example.com/dashboard")
print(response.status_code)
# الخطوة 3: إغلاق الجلسة
session.close()
استخدام Session أيضًا أكثر كفاءة من حيث الأداء: يتم إعادة استخدام اتصال TCP، بدلاً من فتحه من جديد لكل طلب. عند جمع البيانات من 1000+ صفحة، يوفر ذلك زيادة ملحوظة في السرعة.
معالجة الأخطاء، المهلات الزمنية وإعادة المحاولات التلقائية
قد تكون خوادم البروكسي غير متاحة، تستجيب ببطء أو تعيد أخطاء الاتصال. يجب أن يكون السكربت الموثوق لجمع البيانات قادرًا على معالجة هذه الحالات والتبديل تلقائيًا إلى بروكسي آخر عند الفشل.
import requests
import random
import time
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
def fetch_with_retry(url, max_retries=3, timeout=10):
"""
يقوم بإجراء طلب مع تغيير البروكسي تلقائيًا عند حدوث خطأ.
يعيد كائن Response أو None عند نفاد المحاولات.
"""
available_proxies = proxy_list.copy()
random.shuffle(available_proxies)
for attempt, proxy_url in enumerate(available_proxies[:max_retries], 1):
proxies = {"http": proxy_url, "https": proxy_url}
try:
response = requests.get(
url,
proxies=proxies,
timeout=timeout,
headers={"User-Agent": "Mozilla/5.0"}
)
response.raise_for_status() # يثير استثناء عند 4xx/5xx
print(f"✓ نجاح في المحاولة {attempt}")
return response
except requests.exceptions.ProxyError:
print(f"✗ المحاولة {attempt}: البروكسي غير متاح — {proxy_url}")
except requests.exceptions.Timeout:
print(f"✗ المحاولة {attempt}: مهلة — {proxy_url}")
except requests.exceptions.HTTPError as e:
print(f"✗ المحاولة {attempt}: خطأ HTTP {e.response.status_code}")
if e.response.status_code == 403:
print(" → تم حظره، نحاول البروكسي التالي...")
except requests.exceptions.RequestException as e:
print(f"✗ المحاولة {attempt}: خطأ عام — {e}")
time.sleep(1) # فترة راحة بين المحاولات
print(f"✗ جميع {max_retries} المحاولات نفدت لـ {url}")
return None
# الاستخدام
result = fetch_with_retry("https://httpbin.org/ip")
if result:
print(result.json())
انتبه إلى raise_for_status() — هذه الطريقة تلقائيًا تثير استثناء عند حالات HTTP 4xx و 5xx. بدونها، سيعتبر السكربت الرد الناجح حتى لو كان الرمز 403 (حظر) أو 429 (تجاوز حد الطلبات).
بروكسي عبر متغيرات البيئة: تخزين آمن للبيانات
تقرأ مكتبة requests متغيرات البيئة HTTP_PROXY و HTTPS_PROXY تلقائيًا. هذا يسمح بعدم تخزين بيانات الاعتماد في الكود والتبديل بسهولة بين البروكسي دون تغيير السكربت.
تعيين المتغيرات في الطرفية (Linux/macOS):
export HTTP_PROXY="http://user:[email protected]:8080" export HTTPS_PROXY="http://user:[email protected]:8080" export NO_PROXY="localhost,127.0.0.1"
أو عبر ملف .env مع مكتبة python-dotenv:
# ملف .env (أضفه إلى .gitignore!) HTTP_PROXY=http://user:[email protected]:8080 HTTPS_PROXY=http://user:[email protected]:8080
# سكربت Python
from dotenv import load_dotenv
import requests
import os
load_dotenv() # تحميل المتغيرات من .env
# requests تستخدم تلقائيًا HTTP_PROXY و HTTPS_PROXY
response = requests.get("https://httpbin.org/ip")
print(response.json())
# أو بوضوح من متغيرات البيئة:
proxies = {
"http": os.getenv("HTTP_PROXY"),
"https": os.getenv("HTTPS_PROXY"),
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
⚠️ مهم: متغير NO_PROXY
يسمح متغير NO_PROXY باستثناء عناوين معينة من البروكسي. تأكد من إضافته إلى localhost و 127.0.0.1، حتى لا تذهب الطلبات المحلية عبر البروكسي.
سيناريوهات حقيقية: جمع البيانات من الأسواق، العمل مع API والأتمتة
دعونا نلقي نظرة على ثلاثة سيناريوهات عملية يواجهها المطورون بشكل متكرر.
السيناريو 1: جمع الأسعار من السوق
عند مراقبة الأسعار على Wildberries أو Ozon، من المهم محاكاة سلوك المستخدم الحقيقي: تمرير الرؤوس الصحيحة للمتصفح، إضافة تأخيرات بين الطلبات وتدوير IP. تناسب بروكسي مراكز البيانات هذه المهمة بشكل جيد — فهي سريعة ورخيصة عند العمل مع كميات كبيرة من البيانات.
import requests
import time
import random
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "ru-RU,ru;q=0.9",
"Referer": "https://www.wildberries.ru/",
}
PROXIES = [
{"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080"},
{"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080"},
]
def get_product_price(article_id: int) -> dict:
"""يحصل على سعر المنتج حسب الرقم التعريفي من Wildberries."""
url = f"https://card.wb.ru/cards/v1/detail?appType=1&curr=rub&nm={article_id}"
proxies = random.choice(PROXIES)
try:
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=15)
resp.raise_for_status()
data = resp.json()
product = data["data"]["products"][0]
return {
"id": product["id"],
"name": product["name"],
"price": product["salePriceU"] / 100, # السعر بالكوبيك
}
except (requests.RequestException, KeyError, IndexError) as e:
return {"error": str(e)}
# جمع بيانات عدة أرقام تعريف مع تأخير
articles = [12345678, 87654321, 11223344]
for article in articles:
result = get_product_price(article)
print(result)
time.sleep(random.uniform(1.5, 3.0)) # تأخير عشوائي بين 1.5-3 ثواني
السيناريو 2: العمل مع API عبر البروكسي
تحدد بعض واجهات API عدد الطلبات من IP واحد (تحديد المعدل). يسمح توزيع الطلبات بين عدة بروكسي بتجاوز هذا التحديد:
import requests
import itertools
from typing import Optional
class ProxyAPIClient:
"""عميل للعمل مع API عبر تدوير البروكسي."""
def __init__(self, api_key: str, proxy_list: list):
self.api_key = api_key
self.proxy_cycle = itertools.cycle(proxy_list)
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
})
def _get_proxy(self) -> dict:
proxy = next(self.proxy_cycle)
return {"http": proxy, "https": proxy}
def get(self, url: str, **kwargs) -> Optional[dict]:
proxies = self._get_proxy()
try:
resp = self.session.get(url, proxies=proxies, timeout=10, **kwargs)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
print(f"فشل طلب API: {e}")
return None
# الاستخدام
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
client = ProxyAPIClient(api_key="your_api_key", proxy_list=proxy_list)
data = client.get("https://api.example.com/products")
السيناريو 3: اختبار الموقع الجغرافي
غالبًا ما يتحقق المسوقون والمتخصصون في SEO من كيفية ظهور الموقع من مناطق مختلفة. باستخدام البروكسي من المواقع المطلوبة، يمكن أتمتة هذه العملية:
import requests
# بروكسي من مناطق مختلفة
regional_proxies = {
"موسكو": "http://user:[email protected]:8080",
"سانت بطرسبرغ": "http://user:[email protected]:8080",
"نوفوسيبيرسك": "http://user:[email protected]:8080",
"الولايات المتحدة": "http://user:[email protected]:8080",
}
url = "https://example.com/prices"
for region, proxy_url in regional_proxies.items():
proxies = {"http": proxy_url, "https": proxy_url}
try:
resp = requests.get(url, proxies=proxies, timeout=15)
print(f"[{region}] الحالة: {resp.status_code} | "
f"الحجم: {len(resp.content)} بايت")
except requests.RequestException as e:
print(f"[{region}] خطأ: {e}")
أي نوع من البروكسي تختار لمهمتك
يؤثر اختيار نوع البروكسي بشكل مباشر على نجاح مشروعك. قد يعمل بروكسي مركز البيانات الرخيص بشكل ممتاز لبعض المهام ويفشل تمامًا في مهام أخرى. إليك دليل عملي للاختيار:
| المهمة | نوع البروكسي | لماذا |
|---|---|---|
| جمع البيانات من الأسواق (Wildberries، Ozon) | سكنية | تبدو مثل المستخدمين العاديين، وتتعرض للحظر بشكل أقل |
| جمع البيانات المفتوحة، الأخبار | مراكز البيانات | سريعة، رخيصة، مجهولة الهوية بشكل كافٍ |
| العمل مع Facebook API، Instagram | محمول | تثق الشبكات الاجتماعية في IP المحمول أكثر من غيرها |
| اختبار الموقع الجغرافي | سكنية مع استهداف جغرافي | تحديد الموقع بدقة، IP حقيقية من المنطقة المطلوبة |
| جمع البيانات تحت ضغط عالٍ (10k+ طلبات/ساعة) | مراكز البيانات (مجموعة) | السرعة والتكلفة مع كميات كبيرة |
| المصادقة والعمل مع الحسابات | سكنية أو محمولة | أقل محفزات لأنظمة مكافحة الاحتيال |
للمهام التي تتطلب أقصى موثوقية وأقل خطر للحظر عند العمل مع المواقع المحمية، يختار المطورون غالبًا بروكسي محمولة — تستخدم عناوين IP من مشغلي الهواتف المحمولة الحقيقيين (MTS، Beeline، MegaFon)، والتي نادرًا ما تدخل قوائم الحظر.
قائمة التحقق من البروكسي قبل الاستخدام
- ✅ تحقق من IP عبر
httpbin.org/ip— هل عنوانك الحقيقي مرئي؟ - ✅ تحقق من السرعة — يجب ألا يتجاوز وقت الاستجابة 2-3 ثوانٍ
- ✅ تأكد من أن البروكسي ليس في قوائم الحظر عبر
blocklist.deأوipqualityscore.com - ✅ تحقق من الموقع الجغرافي عبر
ipinfo.io— هل يتطابق مع المنطقة المتوقعة؟ - ✅ اختبر على الموقع المستهدف مع طلب واحد قبل تشغيل السكربت الكامل
- ✅ تأكد من أن حركة مرور HTTPS أيضًا تمر عبر البروكسي (كلا المفتاحين في القاموس)
الخاتمة
إعداد البروكسي في Python requests ليس صعبًا، ولكنه يتطلب الانتباه للتفاصيل. المبادئ الرئيسية التي يجب تذكرها: دائمًا حدد كلا المفتاحين (http و https) في قاموس البروكسي، استخدم Session للسيناريوهات متعددة الخطوات، تأكد من معالجة الأخطاء والمهلات الزمنية، واحفظ بيانات الاعتماد في متغيرات البيئة، وليس في الكود.
لجمع البيانات الصناعية مع آلاف الطلبات يوميًا، لا يكفي قائمة البروكسي اليدوية — تحتاج إلى تدوير. إذا كنت تجمع البيانات من أسواق محمية مثل Wildberries أو Ozon، أو تعمل مع الشبكات الاجتماعية أو تختبر الموقع الجغرافي، نوصي بتجربة بروكسي سكنية — فهي توفر مستوى عالٍ من الثقة من أنظمة مكافحة الروبوتات وتدعم تدوير IP تلقائي عبر نقطة نهاية واحدة، مما يبسط بشكل كبير كود السكربت الخاص بك.