لماذا يعمل البروكسي في المتصفح ولكنه لا يعمل في الكود: تحليل شامل للمشكلة
موقف كلاسيكي: قمت بإعداد بروكسي في المتصفح، وفتحت الموقع — كل شيء يعمل. لكنك شغلت سكريبت بنفس البروكسي — خطأ في الاتصال، مهلة (Timeout)، أو حظر. دعونا نحلل سبب حدوث ذلك وكيفية إصلاحه.
ما الفرق بين الطلب من المتصفح والطلب من الكود
عندما تفتح موقعًا في متصفحك عبر بروكسي، يحدث ما هو أكثر بكثير من مجرد طلب HTTP. يقوم المتصفح تلقائيًا بما يلي:
- إرسال مجموعة كاملة من الرؤوس (User-Agent، Accept، Accept-Language، Accept-Encoding)
- تنفيذ مصافحة TLS (TLS-handshake) بمجموعة تشفير صحيحة
- معالجة عمليات إعادة التوجيه (Redirects) وملفات تعريف الارتباط (Cookies)
- تنفيذ JavaScript وتحميل الموارد التابعة
- تخزين إجابات DNS والشهادات مؤقتًا (Caching)
يبدو الطلب الأدنى من الكود مختلفًا تمامًا بالنسبة للخادم — كأنه روبوت وليس إنسانًا. حتى لو كان البروكسي يعمل بشكل صحيح، قد يقوم الموقع المستهدف بحظر السكريبت الخاص بك تحديدًا.
مشاكل مصادقة البروكسي (Authentication)
السبب الأكثر شيوعًا هو النقل غير الصحيح لاسم المستخدم وكلمة المرور. يعرض المتصفح نافذة منبثقة لإدخال بيانات الاعتماد، بينما يجب القيام بذلك بشكل صريح في الكود.
تنسيق URL غير صحيح
الخطأ الشائع هو إغفال المخطط (Scheme) أو عدم ترميز الأحرف الخاصة بشكل صحيح:
# غير صحيح
proxy = "user:pass@proxy.example.com:8080"
# صحيح
proxy = "http://user:pass@proxy.example.com:8080"
# إذا كانت كلمة المرور تحتوي على أحرف خاصة (@, :, /)
from urllib.parse import quote
password = quote("p@ss:word/123", safe="")
proxy = f"http://user:{password}@proxy.example.com:8080"
المصادقة عبر IP مقابل اسم المستخدم/كلمة المرور
يستخدم بعض مزودي البروكسي قائمة بيضاء (Whitelist) بناءً على عنوان IP. يعمل المتصفح على جهازك لأن عنوان IP الخاص بك مُضاف إلى القائمة البيضاء. أما السكريبت الذي يعمل على خادم آخر فلا يعمل لأنه يستخدم عنوان IP مختلفًا.
تحقق من لوحة تحكم مزود الخدمة لمعرفة طريقة المصادقة المستخدمة وعناوين IP المضافة إلى القائمة البيضاء.
عدم تطابق البروتوكولات HTTP/HTTPS/SOCKS
غالبًا ما يحدد المتصفح نوع البروكسي تلقائيًا. في الكود، يجب تحديده بوضوح، والخطأ في البروتوكول يؤدي إلى فشل صامت.
| نوع البروكسي | المخطط في URL | الخصائص |
|---|---|---|
| بروكسي HTTP | http:// |
يعمل لـ HTTP و HTTPS عبر CONNECT |
| بروكسي HTTPS | https:// |
اتصال مشفر مع البروكسي |
| SOCKS4 | socks4:// |
بدون مصادقة، IPv4 فقط |
| SOCKS5 | socks5:// |
مع مصادقة، يدعم UDP و IPv6 |
| SOCKS5h | socks5h:// |
حل أسماء DNS عبر البروكسي |
من الضروري: إذا كان لديك بروكسي SOCKS5، وكنت تستخدم المخطط http://، فلن يتم إنشاء الاتصال. ستحاول المكتبة التحدث ببروتوكول HTTP مع خادم SOCKS.
الرؤوس المفقودة وبصمة المتصفح (Fingerprint)
حتى لو كان البروكسي يعمل بشكل صحيح، قد يحظر الموقع الطلب بسبب الرؤوس المشبوهة. قارن بين:
طلب من المتصفح
GET /api/data HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
طلب افتراضي من مكتبة requests
GET /api/data HTTP/1.1
Host: example.com
User-Agent: python-requests/2.28.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
الفرق واضح. موقع ذو حماية مضادة للروبوتات سيكتشف على الفور أن الطلب لا يأتي من متصفح.
الحد الأدنى من الرؤوس لإخفاء الهوية
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": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Cache-Control": "max-age=0"
}
شهادات SSL والتحقق منها
يحتوي المتصفح على مخزن مدمج لشهادات الجذر (Root Certificates) ويمكنه التعامل مع تكوينات SSL المختلفة. قد تنشأ مشاكل في الكود:
خطأ SSL: CERTIFICATE_VERIFY_FAILED
تستخدم بعض البروكسيات شهاداتها الخاصة لفحص حركة المرور. قد يكون المتصفح يثق بهذه الشهادة، لكن السكريبت الخاص بك لا يثق بها.
# حل مؤقت لأغراض التصحيح (ليس للإنتاج!)
import requests
response = requests.get(url, proxies=proxies, verify=False)
# الحل الصحيح — تحديد مسار الشهادة
response = requests.get(url, proxies=proxies, verify="/path/to/proxy-ca.crt")
مهم: تعطيل التحقق من SSL (
verify=False) يجعل الاتصال عرضة لهجمات MITM. استخدمه فقط للتصحيح في بيئة آمنة.
بصمة TLS (TLS Fingerprint)
تقوم أنظمة مكافحة الروبوتات المتقدمة بتحليل بصمة TLS — ترتيب ومجموعة التشفير عند إنشاء الاتصال. تستخدم مكتبة Python requests مجموعة قياسية تختلف عن تلك المستخدمة في المتصفح.
لتجاوز ذلك، استخدم مكتبات ذات بصمة TLS مخصصة:
# التثبيت: pip install curl-cffi
from curl_cffi import requests
response = requests.get(
url,
proxies={"https": proxy},
impersonate="chrome120" # يحاكي بصمة TLS لمتصفح Chrome 120
)
تسرب DNS وحل الأسماء (DNS Resolving)
مشكلة أخرى غير واضحة هي حل أسماء DNS. عند استخدام بروكسي HTTP، قد يتم إرسال طلب DNS مباشرة من جهازك، متجاوزًا البروكسي.
كيف يؤثر هذا على العمل
- يرى الموقع مُحلل DNS الحقيقي الخاص بك، وليس البروكسي
- يتم تحديد الموقع الجغرافي بشكل غير صحيح
- قد تحظر بعض المواقع عدم تطابق المنطقة الجغرافية لـ IP و DNS
الحل لـ SOCKS5
استخدم المخطط socks5h:// بدلاً من socks5:// — الحرف "h" يعني أن حل أسماء DNS سيتم تنفيذه على جانب البروكسي:
# يتم حل DNS محليًا (تسرب!)
proxy = "socks5://user:pass@proxy.example.com:1080"
# يتم حل DNS عبر البروكسي (صحيح)
proxy = "socks5h://user:pass@proxy.example.com:1080"
أمثلة عملية لـ Python و Node.js و cURL
Python مع requests
import requests
from urllib.parse import quote
# بيانات البروكسي
proxy_host = "proxy.example.com"
proxy_port = "8080"
proxy_user = "username"
proxy_pass = quote("p@ssword!", safe="") # ترميز الأحرف الخاصة
# تشكيل URL البروكسي
proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
proxies = {
"http": proxy_url,
"https": proxy_url
}
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": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
try:
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
headers=headers,
timeout=30
)
print(f"Status: {response.status_code}")
print(f"IP: {response.json()}")
except requests.exceptions.ProxyError as e:
print(f"خطأ البروكسي: {e}")
except requests.exceptions.ConnectTimeout:
print("مهلة الاتصال بالبروكسي")
Python مع aiohttp (غير متزامن)
import aiohttp
import asyncio
async def fetch_with_proxy():
proxy_url = "http://user:pass@proxy.example.com:8080"
async with aiohttp.ClientSession() as session:
async with session.get(
"https://httpbin.org/ip",
proxy=proxy_url,
headers={"User-Agent": "Mozilla/5.0..."}
) as response:
return await response.json()
result = asyncio.run(fetch_with_proxy())
print(result)
Node.js مع axios
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
const proxyUrl = 'http://user:pass@proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);
axios.get('https://httpbin.org/ip', {
httpsAgent: agent,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'
}
})
.then(response => console.log(response.data))
.catch(error => console.error('Error:', error.message));
Node.js مع node-fetch و SOCKS
const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');
const agent = new SocksProxyAgent('socks5://user:pass@proxy.example.com:1080');
fetch('https://httpbin.org/ip', { agent })
.then(res => res.json())
.then(data => console.log(data));
cURL
# بروكسي HTTP
curl -x "http://user:pass@proxy.example.com:8080" \
-H "User-Agent: Mozilla/5.0..." \
https://httpbin.org/ip
# بروكسي SOCKS5 مع حل DNS عبر البروكسي
curl --socks5-hostname "proxy.example.com:1080" \
--proxy-user "user:pass" \
https://httpbin.org/ip
# تصحيح الأخطاء — عرض عملية الاتصال بالكامل
curl -v -x "http://user:pass@proxy.example.com:8080" \
https://httpbin.org/ip
قائمة التحقق للتشخيص
إذا لم يعمل البروكسي في الكود، فتحقق بالترتيب التالي:
- تنسيق URL البروكسي — هل يوجد مخطط (http://، socks5://)؟
- الأحرف الخاصة في كلمة المرور — هل تم ترميزها عبر URL-encoding؟
- نوع البروكسي — هل يتطابق البروتوكول المحدد مع النوع الفعلي؟
- المصادقة — هل هي عبر IP أم عبر اسم مستخدم/كلمة مرور؟ هل IP الخادم في القائمة البيضاء؟
- الرؤوس (Headers) — هل تمت إضافة User-Agent ورؤوس المتصفح الأخرى؟
- SSL — هل هناك أخطاء في الشهادة؟
- DNS — هل تم استخدام socks5h:// لحل الأسماء عبر البروكسي؟
- مهلات الانتظار (Timeouts) — هل هناك وقت كافٍ للاتصال (خاصة لـ البروكسيات السكنية)؟
الخلاصة
يكمن الفرق بين المتصفح والكود في التفاصيل: الرؤوس، البروتوكولات، SSL، و DNS. يخفي المتصفح هذه التعقيدات، بينما يجب إعداد كل جانب بوضوح في الكود. ابدأ بالتحقق من تنسيق URL البروكسي والمصادقة، ثم أضف رؤوس المتصفح — هذا يحل 90% من المشاكل.
بالنسبة لمهام الكشط (Scraping) والأتمتة التي تتطلب استقرارًا ونسبة حظر منخفضة، تعتبر البروكسيات السكنية خيارًا جيدًا — ويمكنك معرفة المزيد عنها على proxycove.com.