Пакет в client/ (зависимость — httpx), установка через pip из git или wheel (make client-build). Тесты против api и воркера из harness. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
9.3 KiB
Markdown
139 lines
9.3 KiB
Markdown
# Использование 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`.
|
||
- Задачи одной сессии выполняются по очереди: вторая ждёт, пока закончится первая.
|
||
- Для параллельной работы заводите несколько сессий, каждую со своим прокси.
|
||
|
||
## Синхронно или через задачи
|
||
|
||
- `/v1/solve` — проще: один запрос, один ответ. Держите соединение открытым до 3–4 минут (см. «Таймауты»).
|
||
- `/v2/tasks` — запрос возвращается сразу, результат забираете по ссылке `GET /v2/tasks/{id}` с той же cookie
|
||
`session_id`. Подходит, когда соединение нельзя держать долго, и для многошаговых сценариев.
|
||
|
||
Многошаговый сценарий (войти, затем несколько раз что-то сделать на сайте) — с удержанием окна:
|
||
|
||
1. `POST /v2/tasks` с `url` и `browser_idle_timeout`: окно останется открытым на это число минут простоя.
|
||
2. Следующие задачи с той же cookie `session_id` и тем же `proxy` выполняются в открытом окне: без запуска Chrome
|
||
и повторного антибота. Без `url` — на текущей странице.
|
||
3. В конце — `DELETE /v2/sessions/current` или задача с `browser_idle_timeout: 0`: окно закроется и освободит слот.
|
||
|
||
Открытое окно занимает слот браузера, поэтому ставьте простой не больше, чем пауза между вашими шагами.
|
||
|
||
## Cookies
|
||
|
||
- Переданные в запросе `cookies` **заменяют** cookies профиля целиком. Не передали — профиль сохраняет свои.
|
||
- Для постоянных cookies указывайте `expires` (unix-время). Cookie без `expires` — сессионная и пропадёт, как
|
||
только закроется окно браузера (после задачи без `browser_idle_timeout`).
|
||
- Обычно передавать cookies не нужно: профиль сам хранит то, что выдал сайт. Передавайте, только если сайт ведёт
|
||
себя по-разному в зависимости от cookies (например, авторизация, полученная вне Robotex).
|
||
|
||
## Ошибки и повторы
|
||
|
||
| Ответ | Что делать |
|
||
|---|---|
|
||
| `503`, `code: no_slots` | `/v1/solve`: все браузеры заняты — повторить через `Retry-After` секунд. Задачи v2 в этом случае ждут в очереди |
|
||
| `challenge_failed` | антибот не пройден за отведённое время: повторить в той же сессии; при повторных неудачах — сменить прокси (и сессию) |
|
||
| `action_failed` | упало действие: смотрите `error` и `actions[].result`; для отладки повторите с `"screenshot": true` |
|
||
| `request_failed` | навигация не удалась: обычно прокси недоступен или сайт не отвечает |
|
||
| `401` | нет ключа в `X-API-Key`, ключ неверный или отозван |
|
||
| `422` | ошибка в запросе: описание в `detail` |
|
||
|
||
Успех — строго `status == "ok"`.
|
||
|
||
## Таймауты
|
||
|
||
Прохождение антибота может занять до 2 минут, плюс навигация (`timeout`) и действия. Таймаут вашего HTTP-клиента
|
||
к Robotex ставьте не меньше `timeout` + 120 с + сумма таймаутов действий. Так же настройте reverse proxy перед сервисом.
|
||
|
||
## Python-клиент
|
||
|
||
Для Python есть готовый пакет `robotex` ([client/README.md](../client/README.md)): задачи, сессии с удержанием окна,
|
||
ожидание результата, ошибки в виде исключений, sync и async.
|
||
|
||
```python
|
||
from robotex import Robotex, Action
|
||
|
||
with Robotex("https://app.robotex.sokolab.xyz", api_key="rbx_...") as rx:
|
||
result = rx.run("https://example.com", [Action.inner_text("title")]).raise_for_status()
|
||
```
|
||
|
||
## Пример (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, headers={"X-API-Key": "rbx_..."})
|
||
|
||
|
||
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
|
||
```
|