feature!: параметр user_agent убран, UA всегда родной; docs/Usage.md
- user_agent в запросе → 422 с подсказкой брать UA из ответа: UA, отличный от реального Chrome, расходится с userAgentData и движком, Cloudflare такой браузер не пропускает (замер: родной 3/3, Chrome 147 и Windows — 0) (#87) - профиль хранит только отпечаток (экран, DPR), UA не подменяется (#89) - docs/Usage.md: практики для клиентов API — тот же UA и прокси, что у браузера, сессии, cookies, ошибки и повторы, таймауты, пример на httpx - README, docs/API.md, docs/Introduction.md; тесты под новый контракт Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0ae5b0c83f
commit
fe509c25c3
11 files changed
+202
-125
No files matched your search
+110
@@ -0,0 +1,110 @@
|
||||
# Использование API: практики для клиентов
|
||||
|
||||
Как встроить Robotex в свой парсер или бота, чтобы антибот проходился стабильно, а полученные cookies работали.
|
||||
Справочник по полям и ответам — [API.md](API.md).
|
||||
|
||||
## Основной сценарий
|
||||
|
||||
Robotex проходит антибот в настоящем браузере и отдаёт то, с чем клиент дальше ходит на сайт **своим HTTP-клиентом**:
|
||||
|
||||
1. `POST /v1/solve` с `url`, своим `proxy` и `captcha_type`.
|
||||
2. Из ответа взять `cookies` (среди них `cf_clearance`) и **`user_agent`**.
|
||||
3. Дальше ходить на сайт своим HTTP-клиентом:
|
||||
- с **тем же User-Agent**, что пришёл в ответе;
|
||||
- через **тот же прокси** (тот же exit-IP);
|
||||
- с полученными cookies.
|
||||
4. Когда сайт снова отвечает челленджем (обычно `403` и страница «Just a moment…») или истекает `cf_clearance`
|
||||
(поле `expires` у cookie) — повторить `/v1/solve` **в той же сессии** (cookie `session_id`).
|
||||
|
||||
`cf_clearance` выдаётся конкретному браузеру: Cloudflare связывает его с User-Agent и, как правило, с IP. С другим UA
|
||||
или через другой прокси cookie не сработает, и сайт снова покажет челлендж.
|
||||
|
||||
## Что задаёт сервис, а не клиент
|
||||
|
||||
User-Agent, размер окна и экран, отпечаток браузера **не настраиваются запросом** — их определяет сам браузер.
|
||||
Клиент подстраивается под ответ, а не наоборот.
|
||||
|
||||
Почему так: подменить можно только часть признаков. Строка UA меняется, но `navigator.userAgentData`, client hints и
|
||||
поведение движка остаются от реального Chrome, и Cloudflare видит расхождение. Замер на Chrome 155 / Linux:
|
||||
|
||||
| User-Agent в браузере | Cloudflare |
|
||||
|---|---|
|
||||
| родной (Linux Chrome 155) | пройден 3 из 3, 7–9 с |
|
||||
| Linux Chrome 147 | не пройден, 3 из 3 |
|
||||
| Windows Chrome 155 | не пройден |
|
||||
|
||||
Поэтому параметр `user_agent` убран из запроса (передача даёт `422`). Раньше по той же причине был убран параметр
|
||||
размера окна. Каждому профилю сервис сам генерирует правдоподобный отпечаток (экран и `devicePixelRatio` из
|
||||
реального распределения) и применяет его средствами Chrome.
|
||||
|
||||
UA в ответе может измениться, когда на сервере обновится Chrome. Не храните UA у себя навсегда — берите его из
|
||||
последнего ответа.
|
||||
|
||||
## Сессии
|
||||
|
||||
- Ответ ставит cookie `session_id`. Передавайте её в следующих запросах: профиль браузера сохраняется (cookies,
|
||||
localStorage, отпечаток), и повторно проходить антибот обычно не нужно — запрос заметно быстрее.
|
||||
- Одна сессия — один «человек»: держите для неё **один и тот же прокси**. Смена exit-IP внутри сессии выглядит
|
||||
подозрительно и обнуляет `cf_clearance`.
|
||||
- Не отправляйте два запроса одной сессии одновременно: профиль Chrome занят первым запросом.
|
||||
- Для параллельной работы заводите несколько сессий, каждую со своим прокси.
|
||||
|
||||
## Cookies
|
||||
|
||||
- Переданные в запросе `cookies` **заменяют** cookies профиля целиком. Не передали — профиль сохраняет свои.
|
||||
- Для постоянных cookies указывайте `expires` (unix-время). Cookie без `expires` — сессионная и пропадёт между
|
||||
запросами, потому что браузер перезапускается на каждый запрос.
|
||||
- Обычно передавать cookies не нужно: профиль сам хранит то, что выдал сайт. Передавайте, только если сайт ведёт
|
||||
себя по-разному в зависимости от cookies (например, авторизация, полученная вне Robotex).
|
||||
|
||||
## Ошибки и повторы
|
||||
|
||||
| Ответ | Что делать |
|
||||
|---|---|
|
||||
| `503`, `code: no_slots` | все браузеры заняты — повторить через `Retry-After` секунд |
|
||||
| `challenge_failed` | антибот не пройден за отведённое время: повторить в той же сессии; при повторных неудачах — сменить прокси (и сессию) |
|
||||
| `action_failed` | упало действие: смотрите `error` и `actions[].result`; для отладки повторите с `"screenshot": true` |
|
||||
| `request_failed` | навигация не удалась: обычно прокси недоступен или сайт не отвечает |
|
||||
| `422` | ошибка в запросе: описание в `detail` |
|
||||
|
||||
Успех — строго `status == "ok"`.
|
||||
|
||||
## Таймауты
|
||||
|
||||
Прохождение антибота может занять до 2 минут, плюс навигация (`timeout`) и действия. Таймаут вашего HTTP-клиента
|
||||
к Robotex ставьте не меньше `timeout` + 120 с + сумма таймаутов действий. Так же настройте reverse proxy перед сервисом.
|
||||
|
||||
## Пример (Python, httpx)
|
||||
|
||||
```python
|
||||
import httpx
|
||||
|
||||
ROBOTEX = "https://app.robotex.sokolab.xyz"
|
||||
PROXY = "http://user:pass@host:port"
|
||||
|
||||
robotex = httpx.Client(base_url=ROBOTEX, timeout=300)
|
||||
|
||||
|
||||
def solve(url: str) -> dict:
|
||||
resp = robotex.post("/v1/solve", json={
|
||||
"url": url,
|
||||
"proxy": PROXY,
|
||||
"captcha_type": "cloudflare",
|
||||
}) # session_id сохраняется в cookies клиента robotex и уходит в следующих запросах
|
||||
data = resp.json()
|
||||
if data.get("status") != "ok":
|
||||
raise RuntimeError(f"{data.get('code')}: {data.get('error')}")
|
||||
return data
|
||||
|
||||
|
||||
solution = solve("https://freelancehunt.com/projects")
|
||||
|
||||
site = httpx.Client(
|
||||
proxy=PROXY, # тот же прокси
|
||||
headers={"User-Agent": solution["user_agent"]}, # тот же UA
|
||||
cookies={c["name"]: c["value"] for c in solution["cookies"]},
|
||||
)
|
||||
page = site.get("https://freelancehunt.com/projects")
|
||||
if page.status_code == 403:
|
||||
solution = solve("https://freelancehunt.com/projects") # та же сессия, обновить cf_clearance
|
||||
```
|
||||
Reference in new issue
Block a user