← كل المقالات

داخل ملف تعريف المتصفح: ما يفعله `unblocker: true` فعليا

علامة واحدة تفعل ثلاثة مفاتيح: ترويسات متصفح حقيقية، وبصمة اتصال مطابقة، وفك ضغط تلقائي لكل من gzip و brotli. إليك ما تفعله ولماذا.

تفشل معظم أدوات الكشط قبل حتى قراءة header واحد.

يفحص الخادم البصمة على مستوى الاتصال التي يرسلها عميلك عبر الشبكة ويقرر ما إذا كنت متصفحا حقيقيا أم مكتبة برمجية تدعي ذلك. مكتبة requests في Python، وحزمة net/http في Go، وأداة curl التقليدية: جميعها تقدم بصمة مميزة بمجرد بدء الاتصال. المواقع التي تهتم بالحماية (مثل Datadome وAkamai وImperva والحلول المدارة من Cloudflare) تقطع الاتصال أو تعرض لك صفحة تحد (challenge page) قبل أن تكتسب قيمة User-Agent أي أهمية.

هذا هو بالضبط ما يعالجه unblocker: true على FourA. خلال الشهر الماضي، حددنا بدقة العناصر الأساسية التي تجعله يعمل بموثوقية عالية.

ما الجديد

إن unblocker: true هو خيار (flag) واحد في أي استدعاء لـ /api/single. عند تفعيله نقوم بثلاثة أمور: إدراج مجموعة headers الخاصة بالمتصفح، وإرسال الـ request عبر طبقة نقل (transport) تطابق تماما ما يرسله المتصفح الحقيقي عبر الشبكة، وفك ضغط أي استجابة يعيدها الخادم (gzip أو brotli أو deflate). كانت الميزتان الأوليان متاحتين منذ مرحلة الإصدار التجريبي (beta). أما الميزة الثالثة (فك الضغط التلقائي لـ brotli) فقد أطلقت في 25 مارس، واكتمل عمل تثبيت الإصدارات (version-pinning) في اليوم التالي للحفاظ على التوافق التام بين الـ headers وطبقة النقل.

آلية العمل

إليك كيف يبدو الـ request:

curl -X POST "https://api.foura.ai/api/single" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "url": "https://example.com/products",
    "method": "GET",
    "unblocker": true
  }'

تعمل ثلاث طبقات في الخلفية.

حقن الترويسات (Header injection). نضبط حزمة ترويسات المتصفح الكاملة: User-Agent و Sec-Ch-Ua و Sec-Ch-Ua-Platform و Sec-Fetch-Site و Sec-Fetch-Mode و Sec-Fetch-Dest و Accept و Accept-Language و Accept-Encoding. الترتيب مهم هنا. المتصفحات الحقيقية ترسل هذه الترويسات بتسلسل محدد، ومكتبات الكشف تتحقق من ذلك.

بصمة الاتصال (Connection signature). يطابق مسار النقل لدينا شكل جلسة المتصفح المحدثة على مستوى البايت: نفس ترتيب الامتدادات، ونفس تفضيلات التشفير (cipher preferences)، ونفس خصائص المصافحة (handshake). ينتج عن cURL القياسي ومكتبة requests في Python و net/http في Go بصمات يتم تمييزها وحظرها تلقائيا خلال أجزاء من الثانية على البنى التحتية المحمية.

فك الضغط التلقائي (Auto-decompression). عند تفعيل unblocker، نضبط Accept-Encoding على gzip, deflate, br ويتولى مسار النقل فك ضغط جسم الاستجابة. تحصل على سلسلة نصية مفكوكة الترميز (أو Buffer إذا مررت returnBuffer: true). لا حاجة للتعامل اليدوي مع brotli، ولا وجود لعدم تطابق بين الترويسات والجسم عندما يختار الموقع deflate بدلا من gzip.

أهمية تثبيت الإصدارات (Version Pinning)

ترتبط بصمات الاتصال بإصدارات محددة. تختلف تفاصيل طبقة النقل للمتصفح هذا الشهر عن الشهر السابق، والموقع الذي يفحص البصمات بدقة سيرصد هذا التباين. نقوم بتثبيت الأجزاء المتغيرة معا لضمان تطابق الترويسات وكائنات navigator وبصمة الاتصال على نفس إصدار المتصفح.

قد يبدو هذا معقدا، وهو كذلك بالفعل. واجهتنا مشكلة بسبب عدم التطابق أثناء عملية نقل الـ monorepo في مارس عندما تم تحديث جزء واحد تلقائيا بينما بقيت الأجزاء الأخرى دون مزامنة. تمثل الحل في عمليتي commit: تثبيت الجزء المتغير، وعدم الاعتماد مطلقا على مدير الحزم للحفاظ على هذا التوافق.

التأثير العملي

في الاختبارات الداخلية على المواقع التي تتحقق من هوية المتصل (الخدمات المالية، السفر، ومواقع التجارة الإلكترونية الكبرى)، يشكل الفرق بين unblocker: false و unblocker: true الفارق بين ظهور صفحة التحدي (challenge page) والحصول على استجابة 200. غالبا ما يتلقى عميل HTTP العادي رمز 403 من المحاولة الأولى. الرابط نفسه باستخدام unblocker: true يجلب الصفحة بنجاح، لأن الطلب يطابق تماما المتصفح الذي يمثله.

أما بالنسبة للمواقع التي لا تفحص البصمات (معظم واجهات برمجة التطبيقات API العامة، وقوالب CMS القديمة، وأي خدمة تعتمد فقط على حدود معدل الطلبات حسب IP rate limits)، فإن ترك unblocker معطلا يعد خيارا ممتازا ويوفر بضعة أجزاء من الثانية في مرحلة التفاوض (negotiation). استخدمه حيث تدعو الحاجة فقط.

للمستخدمين المتقدمين

إليك بعض الأنماط المفيدة.

ادمج unblocker مع residential proxy عندما يتحقق الخادم الهدف من سمعة عنوان IP. عناوين IP الخاصة بمراكز البيانات (Datacenter IPs) حتى مع بصمة اتصال مثالية تظل مكشوفة على شبكات ASN المدرجة في القوائم السوداء للموقع. يقوم endpoint الخاص بخدمة البروكسي لدينا (/api/proxy) بالتدوير تلقائيا حسب نطاق الهدف، لذا يكفي عادة إضافة "proxy": "residential" إلى الطلب.

تجاوز unblocker عند استدعاء JSON APIs التي لا تشترط وجود متصفح. قد تبدو الترويسات الإضافية مشبوهة بالنسبة لواجهة برمجة تطبيقات تتوقع عميلا برمجيا، مثل اتصال خدمة خلفية (backend) بخدمة مصغرة تابعة لها.

إذا كان الموقع يتحقق من الزائر باستخدام JavaScript قبل عرض الصفحة، فلن يكون unblocker وحده كافيًا. ستحتاج إلى endpoint المتصفح، والذي يُشغل JavaScript الخاص بالصفحة في متصفح كامل. هذا منتج مختلف بتسعير رصيد مختلف، وقد شرحناه في مهام المتصفح: كيفية كشط المواقع كثيفة الاعتماد على JavaScript.

ويمكنك دمج unblocker مع كتلة validate لرفض الاستجابات التي تُرجع تقنيًا كود الحالة 200 ولكنها تحتوي على صفحة تحدي (challenge page):

{
  "url": "https://example.com/products",
  "method": "GET",
  "unblocker": true,
  "validate": {
    "data": { "fail": ["captcha", "Access Denied"] }
  }
}

يحول ذلك حالات الفشل الصامتة إلى حالات فشل مصنفة، وهو أمر مهم لتتبع معدل النجاح في dashboard.

ما التالي

تطلق المتصفحات إصدارا مستقرا جديدا كل 4 أسابيع. ونحن نقوم بترقية بنيتنا البرمجية لمواكبة ذلك. لا تحتاج إلى تعديل أي شيء من جانبك: يظل unblocker: true موجها نحو أي إصدار متصفح قمنا بالتحقق منه بالكامل (end-to-end).

العمل الأصعب ينتظرنا في المرحلة القادمة. بدأت عمليات التحقق من HTTP/3 تظهر بالفعل في المواقع الكبرى، كما أن مطابقة بروتوكول النقل QUIC أكثر تعقيدا مقارنة ببروتوكولات النقل الأقدم، وبدأ الانتقال من حزم header الثابتة إلى المحاكاة الديناميكية الحقيقية. انتقلت المواقع المحمية إلى التحقق من ترتيب إطارات HTTP/2 (frame ordering)، والفجوة بين "مكتبة تبدو كمتصفح" و"متصفح فعلي" ستتقلص من كلا الجانبين. سنكتب عن هذا بالتفصيل عند إطلاقه.