Защо proxy заявката изчерпа опитите си
Проблемът
Дадено POST /api/proxy/ извикване се връща с грешка и без данни. Съобщението е кратко и винаги със същата структура:
{
"error": "Download maxTry limit reached",
"total": 34.812,
"request": { "...": "..." }
}
Това изречение звучи по един и същи начин, независимо дали всеки изход е бил блокиран, всеки изход е бил неактивен, или FourA е извлякъл реалната страница при почти всеки опит, а вашите собствени validate правила са я отхвърлили. Тези три случая изискват напълно противоположни решения.
Отговорът: attemptReport
Всеки неуспешен Proxy response съдържа 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 |
integer | Направени опити |
noResponse |
integer | Изходната точка никога не отговори, така че сайтът изобщо не беше достигнат |
defense |
integer | Сайтът отговори и при този отговор беше разпознат доставчик на защита от ботове |
contentRejected |
integer | HTTP 200, без защита от ботове, отхвърлен само от вашето validate.data |
statusRejected |
integer | Сайтът отговори, без защита от ботове, отхвърлен от вашето validate.status |
other |
integer | Получен е отговор и нито едно от горните не важи |
vendors |
string[] | Всеки доставчик на защита от ботове, разпознат някъде в задачата |
profilesTried |
string[] | Браузърните профили, които задачата е изпратила, подредени по ред на първо използване. default означава, че заявката е изпратена точно както сте я написали. |
summary |
string | Едно изречение, съставено от броячите. Безопасно за логване или показване на потребител. |
Разчитане
contentRejected е висока стойност
Страниците пристигнаха. Вашето правило validate.data не съвпадна с тях.
Това е проблемът, който можете да поправите сами, и е този, който всеки друг сигнал скрива: заявките изглеждат като неуспешни във всяка метрика, а FourA през цялото време е доставял реално съдържание. Изтеглете страницата веднъж през POST /api/single/ без никакво validate, вижте какво всъщност се връща и пренапишете правилото спрямо него.
Честа причина е едно правило, приложено към набор от страници, които не са напълно еднакви. Селектор, който съществува на страници със статии, но не и на страници с видеа, се проваля всеки път, когато попадне на страница с видео, постоянно, при пълен разход.
statusRejected е висока стойност
Сайтът отговори и вашето правило validate.status върна отказ. Ако тези статуси са 401, 403, 429 или 503, сайтът отказва достъп на клиента, вместо да съобщава, че страницата липсва. Опитайте:
- Друг браузърен профил (
browser,os,versionвъв вътрешния обектrequest) exitCountries, ако съдържанието е ограничено по регионPOST /api/browser/, ако отказът изисква JavaScript за преодоляване
defense е висока стойност
В отговорите беше разпозната защита от ботове, а vendors посочва коя точно. Вижте Anti-Bot Defenses за това какво FourA преодолява в момента и какво само отчита. Ако доставчикът не е сред тези, които се преодоляват на този endpoint, преместете извикването към POST /api/browser/ или POST /api/auto/.
noResponse е висока стойност
Изходните точки изобщо не отговориха, така че нищо не беше научено за целевия адрес. Увеличете maxTries, увеличете timeout_ms и проверете дали URL адресът се резолвва от публичния интернет.
other е висока стойност
Получен е отговор и не попада в нито една от горните категории. Сравнете total_time с вашия timeout_ms: целеви адрес, който е по-бавен от зададения лимит, попада тук.
Ротация на браузърни профили
Когато даден сайт откаже браузъра, изпратен от FourA, Proxy спира да настоява за него и опитва друго семейство от публичния каталог с профили. Това не струва допълнителен опит: ротацията променя какво изпраща повторният опит, а не дали такъв се извършва.
profilesTried показва как се случва това. Един запис означава, че заявката е изпратена както е описана всеки път. Няколко записа означават, че ротацията е извършена и сайтът е отказал всеки от тях, което е различна ситуация от липсата на ротация изобщо.
При успешен Proxy response полето profile се появява само когато ротацията е избрала браузър, който не сте заявили:
{
"status": 200,
"data": "<!doctype html>...",
"proxy": "A1B2C3",
"profile": "...",
"total": 4.108
}
Стойността е catalogue id от GET /api/profiles. Липсата му означава, че заявката е изпратена точно както е написана. Наличието му означава, че браузърът, който е сработил, не е бил този, който сте посочили, така че подайте това id обратно като profile при следващите повиквания, вместо да повтаряте неуспешния. Playground в таблото за управление прави това автоматично чрез Carry.
Изрично зададен profile, browser, os или version във вашата заявка никога не се презаписва. Същото важи и за заявка, съдържаща ваш собствен хедър User-Agent или Cookie, тъй като разрешението е обвързано със сигнатурата, с която е получено.
Четене в кода
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 request и response
- Anti-Bot Defenses: Доставчици,
defenseи повторно използване на clearance - Request Outcomes: Как отхвърленият response се класифицира и таксува
- Common Issues: Други проблеми и техните решения
- Choosing the Right Endpoint: Кога Proxy не е правилният инструмент