أسباب استنفاد محاولات 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 الأداة المناسبة