أسباب استنفاد محاولات Proxy Request

المشكلة

يعود استدعاء POST /api/proxy/ بخطأ ودون أي بيانات. الرسالة قصيرة وتأتي دائمًا بنفس الشكل:

{
  "error": "Download maxTry limit reached",
  "total": 34.812,
  "request": { "...": "..." }
}

تُقرأ هذه الجملة بالطريقة ذاتها سواء كانت جميع منافذ الخروج محظورة، أو كانت جميع منافذ الخروج معطلة، أو كان FourA قد جلب الصفحة الحقيقية في كل محاولة تقريبًا وتخلصت قواعد validate الخاصة بك منها. تتطلب هذه الحالات الثلاث حلولاً متعاكسة.

الحل: attemptReport

يحمل كل استجابة Proxy فاشلة كائن attemptReport بجانب الخطأ. وهو يحصي ما واجهته المحاولات فعليًا:

{
  "error": "Download maxTry limit reached",
  "attemptReport": {
    "total": 25,
    "noResponse": 0,
    "defense": 0,
    "contentRejected": 25,
    "statusRejected": 0,
    "other": 0,
    "vendors": [],
    "profilesTried": ["default"],
    "summary": "25 attempt(s): 25 returned HTTP 200 with no defense present and were rejected only by your validate.data - the page was fetched, your content rule did not match it"
  },
  "total": 34.812
}

سلسلة error لم تتغير عن قصد، حتى يستمر العميل الذي يطابقها في العمل. اقرأ attemptReport.summary للحصول على إجابة من سطر واحد، أو التعدادات عندما تريد التفرع بناءً عليها.

الحقول

الحقل النوع ما يحسبه
total عدد صحيح المحاولات المنفذة
noResponse عدد صحيح لم تستجب عقدة الخروج مطلقًا، لذا لم يتم الوصول إلى الموقع إطلاقًا
defense عدد صحيح استجاب الموقع وتم التعرف على مزود فحص الروبوتات في تلك الاستجابة
contentRejected عدد صحيح رمز HTTP 200، بدون فحص روبوتات، وتم الرفض فقط بواسطة validate.data الخاص بك
statusRejected عدد صحيح استجاب الموقع، بدون فحص روبوتات، وتم الرفض بواسطة validate.status الخاص بك
other عدد صحيح تم الرد، ولم ينطبق أي مما سبق
vendors مصفوفة سلاسل نصية كل مزودي فحص الروبوتات الذين تم التعرف عليهم في أي مكان ضمن المهمة
profilesTried مصفوفة سلاسل نصية ملفات تعريف المتصفح التي أرسلتها المهمة، بترتيب الاستخدام الأول. تعني default أن الطلب تم إرساله تمامًا كما كتبته.
summary سلسلة نصية جملة واحدة تم إنشاؤها من التعدادات. آمنة للتسجيل في السجلات أو للعرض على المستخدم.

قراءة البيانات

ارتفاع قيمة contentRejected

وصلت الصفحات. قاعدة validate.data الخاصة بك لم تطابقها.

هذه هي الحالة التي يمكنك إصلاحها بنفسك، وهي الحالة التي تخفيها كل الإشارات الأخرى: تبدو الطلبات كإخفاقات في كل المقاييس، بينما كانت FourA تقدم محتوى حقيقيًا طوال الوقت. اجلب الصفحة مرة واحدة عبر POST /api/single/ بدون أي validate على الإطلاق، وانظر إلى ما يعود فعليًا، وأعد كتابة القاعدة بناءً عليه.

السبب الشائع هو تطبيق قاعدة واحدة على مجموعة صفحات ليست متطابقة كلها. فالمحدد (selector) الموجود في صفحات المقالات وغير الموجود في صفحات الفيديو يفشل في كل مرة يصل فيها إلى صفحة فيديو، بشكل دائم وبكامل التكلفة.

ارتفاع قيمة statusRejected

استجاب الموقع وقاعدتك validate.status رفضت الاستجابة. إذا كانت تلك الحالات 401 أو 403 أو 429 أو 503، فإن الموقع يرفض العميل بدلاً من إعلامه بأن الصفحة غير موجودة. جرب:

  • ملف تعريف متصفح آخر (browser أو os أو version في كائن request الداخلي)
  • exitCountries إذا كان المحتوى مقيدًا جغرافيًا
  • POST /api/browser/ إذا كان تجاوز الرفض يتطلب JavaScript

ارتفاع قيمة defense

تم التعرف على فحص روبوتات في الاستجابات، وتحدد vendors هويته. راجع Anti-Bot Defenses لمعرفة ما تتجاوزه FourA حاليًا وما تكتفي بالإبلاغ عنه فقط. إذا لم يكن المزود من النوع الذي يتم تجاوزه على نقطة النهاية هذه، فانقل الاستدعاء إلى POST /api/browser/ أو POST /api/auto/.

ارتفاع قيمة noResponse

لم تستجب عقد الخروج إطلاقًا، لذلك لم يتم التعرف على أي شيء بخصوص الهدف. ارفع قيمة maxTries وارفع قيمة timeout_ms، وتحقق من إمكانية حل عنوان URL من شبكة الإنترنت العامة.

ارتفاع قيمة other

تم الرد، ولم يتم التصنيف وفقًا لأي مما سبق. تحقق من total_time مقابل timeout_ms الخاص بك: الهدف الذي يستجيب ببطء أكبر من ميزانيتك المحددة يقع هنا.

تدوير ملفات تعريف المتصفح

عندما يرفض موقع ما المتصفح الذي أرسلته FourA، يتوقف Proxy عن الإصرار عليه ويجرّب عائلة أخرى من public profile catalogue. لا يكلف هذا أي محاولة إضافية: يغيّر التدوير ما يرسله retry، ولا يغيّر أبدًا ما إذا كانت المحاولة ستتم أم لا.

profilesTried هي الطريقة التي ترى بها حدوث ذلك. يعني الإدخال الواحد أن الـ request أُرسل كما كُتب في كل مرة. وتعني الإدخالات المتعددة أن التدوير قد تم ورفض الموقع كل واحد منها، وهو وضع مختلف تمامًا عن عدم التدوير على الإطلاق.

في استجابة Proxy الناجحة، يظهر حقل profile فقط عندما يختار التدوير متصفحًا لم تطلبه:

{
  "status": 200,
  "data": "<!doctype html>...",
  "proxy": "A1B2C3",
  "profile": "...",
  "total": 4.108
}

القيمة هي catalogue id من GET /api/profiles. الغياب يعني أن الـ request أُرسل تمامًا كما كُتب. الوجود يعني أن المتصفح الذي نجح لم يكن هو الذي كتبته، لذا مرر هذا الـ id مجددًا كـ profile في الاستدعاءات اللاحقة بدلاً من إعادة تشغيل المتصفح الذي فشل. تقوم لوحة التحكم Playground بذلك نيابة عنك باستخدام Carry.

أي تحديد صريح لـ profile أو browser أو os أو version في الـ request الخاص بك لا يتم تجاوزه أبدًا. ولا يتم تجاوز الـ request الذي يحمل header خاصًا بك من نوع User-Agent أو Cookie، لأن الـ clearance مرتبط بالتوقيع الذي حصل عليه.

Reading It in Code

import requests

API = "https://eu.api.foura.ai"
H = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}

r = requests.post(f"{API}/api/proxy/", headers=H, json={
    "maxTries": 5,
    "request": {
        "method": "GET",
        "url": "https://example.com/product/42",
        "validate": {"data": {"accept": ["Add to cart"]}},
    },
}).json()

if "error" in r:
    rep = r.get("attemptReport", {})
    print(rep.get("summary", r["error"]))

    if rep.get("contentRejected", 0) > rep.get("total", 0) / 2:
        # The pages arrived. The validate rule is what threw them away.
        raise SystemExit("validate.data did not match the real page")
    if rep.get("defense", 0):
        print("bot check met:", ", ".join(rep.get("vendors", [])))

ذات صلة

  • API Endpoints: مرجع طلبات واستجابات Proxy الكامل
  • Anti-Bot Defenses: المزودون، وdefense، وإعادة تشغيل تصريح المرور
  • Request Outcomes: كيفية تصنيف الاستجابة المرفوضة واحتساب تكلفتها
  • Common Issues: المشكلات الشائعة الأخرى وحلولها
  • Choosing the Right Endpoint: الحالات التي لا يُعد فيها Proxy الأداة المناسبة
آخر تحديث: 31 أغسطس 2026