ما هو الكشط باستخدام nodriver ولماذا يحل محل undetected-chromedriver؟
الكشط باستخدام nodriver أصبح الخيار المفضل للمهندسين الذين يهاجرون من undetected-chromedriver. إذا كنت تواجه حجوباً متكررة من Cloudflare أو Imperva، فإن nodriver يقدم حلاً جذرياً: لا WebDriver، لا Selenium، لا بصمة CDP مكشوفة. هذه المكتبة Python غير المتزامنة (nodriver async) تتصل مباشرة بـ Chrome عبر بروتوكول Chrome DevTools Protocol عبر websockets، مما يزيل طبقات التجريد التي تكشف الأتمتة.
تنبيه قانوني: هذا الدليل للأتمتة المصرّح بها والبيانات العامة فقط. تأكد من الامتثال لشروط الخدمة و robots.txt وقوانين مثل CFAA الأمريكية و GDPR الأوروبية قبل أي نشاط كشط.
المشكلة الأساسية: متصفحات الأتمتة التقليدية مثل Selenium WebDriver تترك آثاراً يمكن اكتشافها. خاصية navigator.webdriver تُضبط على true، و CDP leaks تكشف أن المتصفح يُتحكم به برمجياً، و User-Agent يحمل علامات HeadlessChrome. أنظمة مثل Cloudflare Bot Management و Imperva Incapsula تكتشف هذه البصمات في أقل من 200ms وتحجب الطلب. nodriver (المعروف سابقاً بـ undetected-chromedriver v2) يعالج هذه المشكلة من الجذور عبر إزالة WebDriver تماماً والتواصل مع Chrome عبر CDP مباشرة.
العمارة: لماذا إزالة WebDriver تحدث فرقاً
في Selenium، يعمل WebDriver كطبقة وسيطة بين كودك والمتصفح. هذه الطبقة تضيف navigator.webdriver = true وتكشف منافذ CDP لأنماط معروفة. nodriver يتخلص من هذه الطبقة بالكامل — كودك Python يتصل مباشرة بـ Chrome عبر websocket باستخدام CDP، تماماً كما يفعل DevTools المدمج في Chrome. النتيجة: متصفح حقيقي بدون بصمات أتمتة.
الفرق الرئيسي بين nodriver undetected وغيره من الحلول هو أنه لا يُرقّع المتصفح بعد التشغيل، بل يمنع ظهور البصمات من البداية. هذا يعني أن فحوصات navigator.webdriver، و window.cdc_، و CDP detection تُرجع نتائج طبيعية بدون أي معالجة لاحقة.
| الميزة | nodriver | undetected-chromedriver | Playwright | Selenium |
|---|---|---|---|---|
| الاعتماد على WebDriver | لا يوجد | نعم (مُرقّع) | لا (CDP) | نعم |
| navigator.webdriver | محذوف | مُرقّع | ظاهر | ظاهر |
| غير متزامن أصلي | نعم | لا | نعم | لا (متزامن) |
| مقاومة الاكتشاف | عالية | متوسطة | منخفضة | منخفضة |
| مصادقة البروكسي عبر CDP | نعم | إضافة | نعم | إضافة |
الواجهة البرمجية الأصلية لـ nodriver
nodriver مبني بالكامل على asyncio. هذا يعني أن كل عملية شبكة وكل تفاعل مع المتصفح غير متزامن. الواجهة بسيطة لكنها قوية: uc.start() تُرجع كائن Browser، وكل صفحة هي كائن Tab مستقل يمكن التحكم به بشكل متزامن مع تبويبات أخرى.
بدلاً من WebDriverWait و expected_conditions في Selenium، يستخدم nodriver نقاط ربط الأحداث (event hooks) و await tab.select() أو await tab.find() التي تنتظر تلقائياً. هذا أكثر كفاءة لأنه لا يتطلب polling.
import nodriver as uc
import asyncio
async def main():
# تشغيل المتصفح — لا حاجة لـ Service أو WebDriver
browser = await uc.start(
headless=False,
browser_args=['--disable-blink-features=AutomationControlled']
)
# فتح صفحة جديدة
tab = await browser.get('https://httpbin.org/headers')
# انتظر ظهور عنصر — لا WebDriverWait
pre = await tab.select('pre')
text = await pre.get_attribute('innerText')
print(text)
# نقاط ربط الأحداث بدلاً من polling
async def on_request(event):
print(f'طلب: {event.request.url}')
browser.add_handler(uc.cdp.network.RequestWillBeSent, on_request)
# التنقل لصفحة أخرى
tab2 = await browser.get('https://httpbin.org/ip')
ip_text = await (await tab2.select('pre')).get_attribute('innerText')
print(ip_text)
browser.stop()
asyncio.run(main())
النقطة المهمة: browser.get() تُرجع كائن Tab جديد لكل صفحة. يمكنك إدارة عشرات التبويبات بشكل متزامن دون حظر event loop. هذا يختلف جذرياً عن Selenium حيث كل تفاعل يحجب الخيط.
تكوين nodriver proxy مع ProxyHat
nodriver لا يحتوي على مدير بروكسيات مدمج. هذا قرار تصميمي مقصود — إبقاء المكتبة خفيفة. بدلاً من ذلك، تمرّر البروكسي عبر browser_args باستخدام --proxy-server، وتعالج المصادقة عبر CDP Fetch domain. هذا النهج أكثر مرونة ويسمح بتغيير البروكسي لكل تبويب أو سياق.
لماذا البروكسيات السكنية ضرورية لـ nodriver؟ لأن nodriver يدير متصفحاً حقيقياً ببصمة متقدمة. ربط هذا المتصفح ببروكسي مركز بيانات يلغي ميزة التخفي — Cloudflare يتحقق من نوع IP (سكني vs مركز بيانات) قبل حتى فحص البصمة. البروكسيات السكنية من مواقع ProxyHat توفر IPs من مزودي إنترنت منزليين حقيقيين، مما يطابق سلوك مستخدم فعلي.
بناء تكوين البروكسي
أولاً، ننشئ دالة مساعدة تبني تكوين ProxyHat. هذا يفصل المنطق عن كود الكشط ويسهل التبديل بين الدول والجلسات:
def build_proxyhat_config(country='US', session=None, socks5=False):
host = 'gate.proxyhat.com'
port = 1080 if socks5 else 8080
scheme = 'socks5' if socks5 else 'http'
# بناء اسم المستخدم مع أعلام Geo-targeting والجلسة
username = 'user'
if country:
username += f'-country-{country}'
if session:
username += f'-session-{session}'
password = 'YOUR_PROXYHAT_PASSWORD'
return {
'host': host,
'port': port,
'scheme': scheme,
'username': username,
'password': password,
'proxy_arg': f'--proxy-server={scheme}://{host}:{port}',
}
هذا التكوين يستخدم بوابة ProxyHat على gate.proxyhat.com:8080 للـ HTTP أو gate.proxyhat.com:1080 للـ SOCKS5. أعلام country-US و session-abc123 تُمرر في اسم المستخدم للتحكم في الموقع الجغرافي والجلسة اللاصقة.
مثال عملي قابل للتشغيل: كشط صفحة محمية
المثال التالي يطلق nodriver عبر نقطة نهاية ProxyHat سكنية بجلسة لاصقة في الولايات المتحدة، يتعامل مع مصادقة البروكسي عبر CDP، وينقل JSON من صفحة محمية:
import nodriver as uc
import asyncio
import json
def build_proxyhat_config(country='US', session=None, socks5=False):
host = 'gate.proxyhat.com'
port = 1080 if socks5 else 8080
scheme = 'socks5' if socks5 else 'http'
username = 'user'
if country:
username += f'-country-{country}'
if session:
username += f'-session-{session}'
return {
'host': host,
'port': port,
'scheme': scheme,
'username': username,
'password': 'YOUR_PROXYHAT_PASSWORD',
'proxy_arg': f'--proxy-server={scheme}://{host}:{port}',
}
async def main():
config = build_proxyhat_config(country='US', session='abc123')
browser = await uc.start(
headless=False,
browser_args=[config['proxy_arg']]
)
# تفعيل Fetch domain لمعالجة مصادقة البروكسي
await browser.send(
uc.cdp.fetch.enable(handle_auth_requests=True)
)
# معالجة تحديات المصادقة
async def on_auth_required(event):
await browser.send(
uc.cdp.fetch.continue_with_auth(
request_id=event.request_id,
auth_challenge_response=(
uc.cdp.fetch.AuthChallengeResponse(
response='ProvideCredentials',
username=config['username'],
password=config['password'],
)
),
)
)
browser.add_handler(
uc.cdp.fetch.AuthRequired, on_auth_required
)
# التنقل لصفحة محمية
tab = await browser.get('https://httpbin.org/json')
await tab.wait_for(selector='pre')
raw = await (await tab.select('pre')).get_attribute('innerText')
data = json.loads(raw)
print(json.dumps(data, indent=2, ensure_ascii=False))
browser.stop()
asyncio.run(main())
هذا المثال يوضح النمط الصحيح: --proxy-server يحدد البروكسي فقط، و CDP Fetch.authRequired يوفر بيانات الاعتماد. لا حاجة لإضافات Chrome أو ملفات PAC. راجع وثائق ProxyHat للحصول على بيانات اعتمادك.
التوسع: حركة متزامنة وأسطول حاويات
قوة nodriver الحقيقية تظهر عند تشغيل تبويبات متعددة بشكل متزامن. كل Tab مستقل، و asyncio.gather يشغلها بالتوازي. لكن انتبه: كل تبويب يستهلك حوالي 150-300 MB من الذاكرة، لذا حدد التزامن بناءً على موارد خادمك.
تبويبات متزامنة مع بروكسيات مختلفة
import nodriver as uc
import asyncio
async def scrape_with_proxy(browser, url, proxy_config):
# تفعيل مصادقة البروكسي
await browser.send(
uc.cdp.fetch.enable(handle_auth_requests=True)
)
async def on_auth(event):
await browser.send(
uc.cdp.fetch.continue_with_auth(
request_id=event.request_id,
auth_challenge_response=(
uc.cdp.fetch.AuthChallengeResponse(
response='ProvideCredentials',
username=proxy_config['username'],
password=proxy_config['password'],
)
),
)
)
browser.add_handler(
uc.cdp.fetch.AuthRequired, on_auth
)
tab = await browser.get(url)
await tab.wait_for(selector='body')
content = await tab.get_content()
return content
async def main():
configs = [
build_proxyhat_config(country='US', session='s1'),
build_proxyhat_config(country='DE', session='s2'),
build_proxyhat_config(country='GB', session='s3'),
]
urls = [
'https://httpbin.org/headers',
'https://httpbin.org/ip',
'https://httpbin.org/user-agent',
]
browser = await uc.start(
headless=True,
browser_args=[configs[0]['proxy_arg']]
)
tasks = [
scrape_with_proxy(browser, url, cfg)
for url, cfg in zip(urls, configs)
]
results = await asyncio.gather(*tasks)
for r in results:
print(len(r), 'bytes')
browser.stop()
asyncio.run(main())
أسطول حاويات Docker
للتوسع إلى 50-100 جلسة متزامنة، شغّل حاويات Docker منفصلة لكل عامل. كل حاوية تدير مثيل Chrome واحد مع بروكسيه الخاص:
FROM python:3.12-slim
RUN apt-get update && apt-get install -y \
chromium \
&& rm -rf /var/lib/apt/lists/*
RUN pip install nodriver
COPY scraper.py /app/scraper.py
WORKDIR /app
ENV PROXYHAT_USER=user-country-US
ENV PROXYHAT_PASS=YOUR_PASSWORD
CMD ['python', '-u', 'scraper.py']
شغّل حاويات متعددة عبر docker-compose أو Kubernetes، مع تمرير متغيرات بيئة مختلفة لكل حاوية (PROXYHAT_USER و PROXYHAT_PASS). هذا يوفر عزلاً كاملاً — إذا تعطل مثيل Chrome واحد، لا يؤثر على البقية. استهدف معدل نجاح 99.9% عبر إضافة retry logic و circuit breakers.
الإغلاق الآمن
أغلق المتصفح دائماً بشكل صريح. استخدم try/finally أو context manager لضمان عدم بقاء عمليات Chrome معلقة:
async def safe_scrape(url):
browser = await uc.start(headless=True)
try:
tab = await browser.get(url)
await tab.wait_for(selector='body')
return await tab.get_content()
finally:
browser.stop()
متى لا تستخدم متصفحاً كاملاً
nodriver قوي لكنه مكلف. كل مثيل Chrome يستهلك 200-500 MB من الذاكرة ووقت تشغيل 2-5 ثوانٍ. إذا كان هدفك مجرد جلب JSON من API عام أو صفحة HTML ثابتة، فاستخدام HTTP مباشر مع curl_cffi أرخص وأسرع بـ 10 أضعاف:
from curl_cffi import requests
response = requests.get(
'https://httpbin.org/headers',
proxies={
'http': 'http://user-country-US-session-abc123:PASSWORD@gate.proxyhat.com:8080',
'https': 'http://user-country-US-session-abc123:PASSWORD@gate.proxyhat.com:8080',
},
impersonate='chrome120',
)
print(response.json())
أو عبر curl مباشرة:
curl -x http://user-country-US-session-abc123:PASSWORD@gate.proxyhat.com:8080 \
https://httpbin.org/headers
القاعدة: استخدم nodriver فقط عندما يحتاج الموقع إلى تنفيذ JavaScript معقد أو تفاعل DOM حقيقي. للـ تتبع نتائج SERP أو مراقبة الأسعار البسيطة، HTTP + curl_cffi يكفي. راجع أسعار ProxyHat لاختيار الخطة المناسبة لحجم عملياتك.
الاعتبارات الأخلاقية
الأتمتة ليست مشكلة في حد ذاتها — المشكلة هي الأتمتة غير المصرّح بها. اتبع هذه المبادئ:
- احترم
robots.txtوCrawl-delay. - لا تتجاوز حدود المعدل (rate limits) بشكل مفرط — استهدف 1-3 طلبات في الثانية لكل IP.
- لا تخزن بيانات شخصية دون أساس قانوني (GDPR Article 6).
- لا تتخطى أنظمة الدفع أو المصادقة.
- توثيق من مستودع nodriver الرسمي يؤكد أن المكتبة مخصصة للأتمتة المشروعة.
لمزيد من حالات الاستخدام، راجع دليل كشط الويب.
النقاط الرئيسية
خلاصة:
- nodriver يزيل WebDriver بالكامل، مما يقلل اكتشاف الأتمتة بنسبة تتجاوز 50% مقارنة بـ Selenium.
- البروكسي يُمرر عبر
--proxy-serverوالمصادقة عبر CDPFetch.authRequired— لا إضافات مطلوبة.- البروكسيات السكنية ضرورية: IP من مركز بيانات يكشف المتصفح فوراً.
- التوسع يتم عبر
asyncio.gatherللتبويبات المتزامنة أو حاويات Docker للعزل الكامل.- للأهداف البسيطة، استخدم
curl_cffiمع ProxyHat — أرخص وأسرع بـ 10 أضعاف.
الأسئلة الشائعة
ما هو الكشط باستخدام nodriver؟
الكشط باستخدام nodriver هو استخدام مكتبة nodriver Python، الخليفة غير المتزامن لـ undetected-chromedriver، لأتمتة متصفح Chrome عبر بروتوكول Chrome DevTools Protocol مباشرة. على عكس Selenium، لا تعتمد nodriver على WebDriver، مما يزيل بصمة navigator.webdriver ويقلل من تسريبات CDP التي تكتشفها أنظمة مكافحة البوت مثل Cloudflare و Imperva.
لماذا يهم الكشط باستخدام nodriver لمستخدمي البروكسيات؟
nodriver لا يحتوي على مدير بروكسيات مدمج، لذا يحتاج المستخدمون إلى تكوين البروكسي عبر browser_args ومعالجة المصادقة عبر CDP. البروكسيات السكنية ضرورية لأن nodriver يدير متصفحاً حقيقياً ببصمة متقدمة، وربطه ببروكسي مركز بيانات يلغي ميزة التخفي. البروكسيات السكنية توفر IPs من مزودي إنترنت فعليين، مما يطابق سلوك مستخدم حقيقي.
أي نوع من البروكسيات يعمل بشكل أفضل مع nodriver؟
البروكسيات السكنية هي الأفضل لـ nodriver لأنها توفر IPs من مزودي إنترنت منزليين حقيقيين، مما يطابق بصمة المتصفح الحقيقي. البروكسيات الدوارة مع جلسات لاصقة (sticky sessions) مثالية للحفاظ على حالة الجلسة عبر طلبات متعددة. تجنب بروكسيات مراكز البيانات لأن Cloudflare و Imperva يكتشفونها بسرعة عبر قواعد بيانات ASN.
كيف تتجنب الحجب عند تنفيذ الكشط باستخدام nodriver؟
استخدم بروكسيات سكنية دوارة مع جلسات لاصقة، أضف تأخيرات عشوائية بين الطلبات (1-3 ثوانٍ)، احترم robots.txt، وحدد عدد التبويبات المتزامنة. اضبط معاملات المتصفح بشكل واقعي، وتجنب الأنماط المتوقعة. استخدم ميزة Geo-targeting لربط IP بالموقع الجغرافي المناسب للهدف، وبدّل الجلسات بشكل دوري.






