الكشط باستخدام nodriver: دليل عملي للمهندسين مع بروكسي ProxyHat

تعلّم كيف تستبدل undetected-chromedriver بـ nodriver — خليفته غير المتزامن القائم على CDP — وتدمجه مع بروكسي سكني من ProxyHat لتجاوز Cloudflare وImperva بكفاءة وقابلية للتوسع.

Scraping With nodriver: A Practical Guide to Async CDP + Residential Proxies

تنبيه قانوني: هذا الدليل موجّه لجمع البيانات العامة المصرّح بها فقط. قد يخضع الكشط غير المصرّح به لقوانين مثل CFAA في الولايات المتحدة وGDPR في الاتحاد الأوروبي. راجع شروط استخدام الموقع المستهدف واستشر مختصاً قانونياً عند الحاجة.

إذا كنت من مهندسي بايثون الذين اعتمدوا على undetected-chromedriver لسنوات، فربما لاحظت أنه يفقد فعاليته تدريجياً أمام أنظمة مثل Cloudflare Turnstile وImperva Incapsula. السبب الجوهري هو أن Selenium WebDriver يترك بصمات بروتوكول واضحة — رأس webdriver في navigator، اتصالات CDP مكشوفة، ومسارات عملية قابلة للكشف. هنا يأتي دور الكشط باستخدام nodriver: مكتبة بايثون غير المتزامنة بالكامل من نفس مطوّر undetected-chromedriver، تعمل مباشرة فوق Chrome DevTools Protocol عبر websockets دون أي طبقة WebDriver.

ما هو الكشط باستخدام nodriver ولماذا يهمك

nodriver هو خليفة undetected-chromedriver كما يوضّحه المستودع الرسمي. بدلاً من توريد Selenium وإجراء تعديلات سطحية على ملفات Chrome، يتصل nodriver مباشرة بـ CDP عبر websocket ثنائي الاتجاه. هذا يعني أن المتصفح الذي تتحكم به لا يملك كائن driver ولا يطابق بصمة WebDriver الكلاسيكية التي تبحث عنها معظم أنظمة مكافحة البوت.

لماذا يهم مستخدمي البروكسي؟ لأن الأنظمة المتقدمة لا تكتشف الـ IP فقط بل تربطه بسلوك المتصفح. إذا كان الـ IP سكنياً موثوقاً لكن المتصفح يكشف عن navigator.webdriver === true، فسيُحجب الطلب. العكس صحيح أيضاً: متصفح نظيف خلف IP مركز بيانات يفشل بنفس القدر. التكامل بين nodriver undetected وبروكسي سكني هو ما يحقق نسب نجاح تتجاوز 95% في صفحات محمية بشدة، مقارنة بـ 40–60% مع WebDriver التقليدي خلف نفس البروكسي.

معمارية nodriver: لماذا تتجاوز Selenium

لفهم قوة nodriver، يجب فهم ما يزيله:

  • لا Selenium ولا WebDriver: لا توجد عملية chromedriver وسيطة، ولا طلبات HTTP JSON Wire Protocol. كل اتصال يمر عبر Chrome DevTools Protocol مباشرة.
  • إزالة navigator.webdriver: nodriver يحقن سكربتات قبل تحميل أي صفحة لتفريغ هذه الخاصية وتعديل بصمات أخرى مثل chrome.runtime وpermissions.query.
  • إخفاء تسريبات CDP: المتصفحات التي تُدار عبر CDP تظهر علامات مثل window.cdc_ أو منافذ تصحيح مكشوفة. nodriver يعالج هذه التسريبات على مستوى الإطلاق.
  • أداء غير متزامن: لأن كل شيء مكتوب بـ asyncio، يمكنك تشغيل عشرات التبويبات المتزامنة في عملية واحدة دون خيوط عائقية.

النتيجة: بصمة متصفح أقرب إلى مستخدم حقيقي، وزمن استجابة أقل لأنك تحذف طبقة وسيطة كاملة. في اختبارات عملية، يقلل nodriver زمن الإقلاع بنحو 300–500ms مقارنة بـ undetected-chromedriver لأنه لا ينتظر تهيئة chromedriver.

الواجهة البرمجية الأساسية لـ nodriver async

واجهة nodriver مختلفة جذرياً عن Selenium. لا توجد driver.get() أو WebDriverWait. بدلاً من ذلك، تعمل مع كائنات Browser وTab وتستخدم await في كل مكان.

import nodriver as uc
import asyncio

async def main():
    browser = await uc.start(headless=False)
    tab = await browser.get("https://example.com")
    # انتظر ظهور عنصر بدلاً من WebDriverWait
    element = await tab.select("h1", timeout=10)
    print(await element.get_attribute("textContent"))
    browser.stop()

if __name__ == "__main__":
    asyncio.run(main())

النقاط الأساسية:

  • uc.start() تُرجع كائن Browser — هذا هو نقطة الدخول الوحيدة.
  • browser.get(url) تفتح تبويباً جديداً وتُرجع كائن Tab.
  • tab.select(selector, timeout=) تحل محل WebDriverWait + expected_conditions — تنتظر حتى يظهر العنصر أو تنتهي المهلة.
  • tab.find() تبحث دون انتظار، مفيدة للعناصر الموجودة مسبقاً.
  • خطافات الأحداث: بدلاً من الاستطلاع الدوري، يمكنك تسجيل معالجات لأحداث CDP مثل Network.responseReceived.
async def on_response(event):
    if event.response.mimeType == "application/json":
        body = await event.response.body
        print(f"JSON caught: {body[:200]}")

tab.add_handler(ur.event.Network.ResponseReceived, on_response)
await tab.get("https://api.example.com/data")

هذا النمط غير المتزامن هو جوهر nodriver async: لا حظر، لا استطلاع، استجابة فورية للأحداث.

إعداد بروكسي nodriver مع ProxyHat

nodriver لا يملك نظام تدوير بروكسي مدمج — وهذا متعمد. المكتبة تركز على التحكم بالمتصفح، أما توزيع الطلبات فيقع على عاتق البنية التحتية الخارجية. الطريقة الموصى بها هي تمرير --proxy-server= عبر browser_args عند الإقلاع.

لأن Chrome لا يدعم مصادقة البروكسي عبر سطر الأوامر، نستخدم إضافة صغيرة أو — وهو الأنظف — نعتمد على بروكسي بوابة تتعامل مع المصادقة في طبقة URL. مع ProxyHat، تُمرّر بيانات الاعتماد داخل اسم المستخدم نفسه.

import nodriver as uc
import urllib.parse

PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "user-country-US-session-abc123"
PROXY_PASS = "pass"

# nodriver يقبل --proxy-server فقط (بدون مصادقة في CLI)
# لذا نبني بروكسي محلي بسيط أو نستخدم إضافة مصادقة
proxy_arg = f"--proxy-server=http://{PROXY_HOST}:{PROXY_PORT}"

browser = await uc.start(
    headless=False,
    browser_args=[proxy_arg],
)

لحل مشكلة المصادقة بشكل أنيق، استخدم بروكسي محلي وسيط (local proxy bridge) يضيف رأس Proxy-Authorization تلقائياً، أو اعتمد على إضافة Chrome مصغّرة. البوابة في gate.proxyhat.com:8080 تدعم HTTP، ومنفذ 1080 يدعم SOCKS5 عندما تحتاج طبقة نقل أعمق.

لماذا البروكسي السكني ضروري للوضع الخفي (stealth)؟ لأن Cloudflare يصنّف نطاقات IP حسب ASN. عناوين مراكز البيانات (مثل AWS أو OVH) تحصل على درجة مخاطرة عالية فوراً، حتى لو كان المتصفح نظيفاً تماماً. البروكسي السكني يربط طلبك بـ ASN مزود إنترنت حقيقي، مما يخفض احتمالية تحدي CAPTCHA بشكل كبير. اطلّع على مواقع ProxyHat لاختيار الدول والمدن المناسبة.

مثال عملي قابل للتشغيل: الكشط من صفحة محمية

سنطلق nodriver عبر نقطة نهاية سكنية أمريكية بجلسة ثابتة، نتنقّل لصفحة محمية، ونلتقط استجابة JSON من الشبكة.

import nodriver as uc
import nodriver.cdp.network as network
import asyncio
import json

PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080

# علم الجلسة الثابتة في اسم المستخدم
PROXY_USER = "user-country-US-session-abc123"
PROXY_PASS = "pass"

async def scrape_protected():
    proxy_arg = f"--proxy-server=http://{PROXY_HOST}:{PROXY_PORT}"

    browser = await uc.start(
        headless=False,
        browser_args=[
            proxy_arg,
            "--disable-blink-features=AutomationControlled",
        ],
    )

    tab = await browser.get("https://protected-target.example")

    captured = []

    async def on_response(event):
        try:
            if event.response.url.endswith("/api/products"):
                body = await tab.send(
                    network.GetResponseBody(event.request_id)
                )
                captured.append(json.loads(body))
        except Exception:
            pass

    tab.add_handler(
        uc.cdp.network.ResponseReceived,
        on_response,
    )

    # تفاعل لتفعيل طلب XHR
    button = await tab.select("#load-more", timeout=15)
    await button.click()

    await tab.wait_for("#results-loaded", timeout=20)

    print(f"Captured {len(captured)} JSON payloads")
    browser.stop()

if __name__ == "__main__":
    asyncio.run(scrape_protected())

النقطة المهمة هنا: session-abc123 تضمن بقاء نفس الـ IP طوال الجلسة. هذا حيوي لأن تبديل الـ IP في منتصف التفاعل (مثلاً بعد تسجيل الدخول) يُعتبر سلوكاً مشبوهاً ويؤدي إلى حظر فوري. عند الحاجة إلى IP جديد، غيّر قيمة الجلسة وأعد الإقلاع.

لمزيد من أنماط الكشط المتقدمة، راجع دليل استخدامات الكشط وتتبّع نتائج البحث.

التوسع: أسطول متزامن في Docker

عندما تحتاج إلى مئات الطلبات المتوازية، يصبح تشغيل متصفح واحد غير كافٍ. نمط التوسع الموصى به مع nodriver هو: حاوية لكل عامل، كل عامل يدير عدة تبويبات، وكل تبويب يملك جلسة بروكسي مستقلة.

التزامن داخل عملية واحدة

import nodriver as uc
import asyncio

SESSIONS = [
    "user-country-US-session-s1",
    "user-country-DE-session-s2",
    "user-country-GB-session-s3",
]
PROXY_PASS = "pass"

async def worker(session_user):
    proxy_arg = f"--proxy-server=http://gate.proxyhat.com:8080"
    browser = await uc.start(
        headless=True,
        browser_args=[proxy_arg, "--no-sandbox"],
    )
    tab = await browser.get("https://target.example")
    title = await tab.evaluate("document.title")
    print(f"{session_user}: {title}")
    browser.stop()

async def main():
    await asyncio.gather(*[worker(s) for s in SESSIONS])

asyncio.run(main())

حاويات Docker

للتوسع الأفقي، شغّل حاويات متعددة من صورة Chrome headless. كل حاوية تدير عملية بايثون مستقلة بتبويباتها الخاصة. استخدم مدير حاويات مثل Docker Compose أو Kubernetes لتوزيع الحمل.

FROM python:3.12-slim
RUN apt-get update && apt-get install -y chromium
RUN pip install nodriver
COPY scraper.py /app/scraper.py
WORKDIR /app
CMD ["python", "scraper.py"]

أفضل الممارسات:

  • حدّ التزامن لكل حاوية: 3–5 تبويبات كحد أقصى لتجنّب استهلاك الذاكرة. كل تبويب يستهلك نحو 150–250MB.
  • إيقاف سلس: استخدم browser.stop() دائماً داخل finally لمنع تسرب عمليات Chrome.
  • تخصيص بروكسي لكل سياق: مرّر --proxy-server مختلف لكل عامل لتوزيع الجغرافيا.
  • مراقبة: تتبّع معدل النجاح وزمن الاستجابة لكل جلسة وبدّل الجلسات الراكدة.

راجع وثائق ProxyHat لتفاصيل حدود التزامن وعدد الجلسات لكل خطة، وصفحة الأسعار لاختيار الخطة المناسبة لحجم أسطولك.

متى لا تستخدم متصفحاً كاملاً

رغم قوة nodriver، ليس كل مهمة كشط تستحق متصفحاً كاملاً. المتصفح الخفي يستهلك ذاكرة ووحدة معالجة كبيرة، ويبطئ الإنتاجية مقارنة بالطلبات HTTP المباشرة.

المعيارnodriver + بروكسي سكنيcurl_cffi + بروكسي سكني
استهلاك الذاكرة لكل مهمة150–250MB~20MB
الطلبات في الثانية (عقدة واحدة)5–15200–500
تجاوز Cloudflare JS Challengeنعم (تنفيذ JS كامل)جزئي (TLS fingerprint فقط)
تكلفة البنية التحتيةمرتفعة (حاويات Chrome)منخفضة جداً
مناسب لـصفحات SPA محمية بشدةAPIs وصفحات HTML ثابتة

القاعدة العملية: ابدأ بـ curl_cffi أو httpx مع بروكسي سكني. إذا واجهت تحدي JS لا يمكن حله بـ TLS fingerprint، انتقل إلى nodriver. لا تستخدم متصفحاً كاملاً لجلب HTML ثابت من موقع غير محمي — هذا إهدار للموارد.

أخطاء شائعة وحالات حدّية

  • نسيان browser.stop(): يؤدي إلى تراكم عمليات Chrome zombie. استخدم دائماً try/finally أو مدير سياق.
  • تغيير الـ IP في منتصف الجلسة: بعد تسجيل الدخول، أي تبديل IP يُسجّل كجلسة مشبوهة. ثبّت session-* طوال التفاعل.
  • استخدام headless بدون تعديلات: وضع headless النقي يكشف عن بصمات. nodriver يخفّي بعضها، لكن أضف --disable-blink-features=AutomationControlled دائماً.
  • تجاهل robots.txt: حتى مع متصفح غير مكشوف، احترام robots.txt وحدود المعدل مسألة أخلاقية وقانونية.
  • عدم التعامل مع CAPTCHA: حتى مع nodriver + بروكسي سكني، قد تظهر تحديات CAPTCHA. خطّط لخدمة حلّ CAPTCHA أو إعادة المحاولة بجلسة جديدة.

نقاط رئيسية

الخلاصة:

  • nodriver يلغي WebDriver ويتصل بـ CDP مباشرة، فيزيل navigator.webdriver وتسريبات CDP.
  • الواجهة غير متزامنة بالكامل: uc.start()، tab.select()، وخطافات الأحداث بدلاً من WebDriverWait.
  • البروكسي يُمرّر عبر --proxy-server=gate.proxyhat.com:8080 مع جلسات ثابتة في اسم المستخدم.
  • البروكسي السكني ضروري لأن أنظمة مكافحة البوت تصنّف ASN قبل فحص المتصفح.
  • للتوسع: حاوية لكل عامل، 3–5 تبويبات لكل حاوية، إيقاف سلس دائماً.
  • لا تستخدم متصفحاً كاملاً عندما يكفي curl_cffi — وفّر الموارد.

الجمع بين nodriver وبروكسي سكني من ProxyHat يمنحك أفضل ما في العالمين: بصمة متصفح نظيفة وIP موثوق. ابدأ بتجربة مجانية على صفحة الأسعار وادمج نقطة النهاية gate.proxyhat.com:8080 في سكربت nodriver التالي لديك.

¿Listo para empezar?

Accede a más de 50M de IPs residenciales en más de 148 países con filtrado impulsado por IA.

Ver preciosProxies residenciales
← Volver al Blog