Files
robotex/docs/Usage.md
T
protokeyandClaude Opus 5.5 7b511d5d38 feature: архитектура v2 — api + очередь + worker, удержание окна браузера (#78)
- Сервис api (FastAPI, без Chrome): POST /v2/tasks, GET /v2/tasks/{id} (доступ по cookie
  сессии), DELETE /v2/sessions/current; /v1/solve — прежний контракт поверх очереди.
- Сервис worker (dramatiq + RabbitMQ): слоты браузера, окна сессий между задачами
  (browser_idle_timeout 0–120 мин, простой — TTL ключа в Redis), heartbeat.
- Маршрутизация задач сессии в персональную очередь воркера с открытым окном.
- Redis: задачи, результаты (RESULT_TTL_HOURS), окна, воркеры. Прокси и ключ 2captcha
  в Redis не сохраняются.
- Движок: AsyncStealthySession.run() — действия на текущей странице без навигации.
- Два образа (robotex-api, robotex-worker), compose с rabbitmq и redis,
  make up/down/logs/smoke, релиз собирает все образы.
- Тесты: api + worker в одном процессе (fakeredis), настоящий воркер dramatiq на
  StubBroker, браузерные тесты на новой схеме, smoke для запущенного стенда.
- Документация: README, API (v2), Deployment, Usage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:13:52 +04:00

8.7 KiB
Raw Blame History

Использование API: практики для клиентов

Как встроить Robotex в свой парсер или бота, чтобы антибот проходился стабильно, а полученные cookies работали. Справочник по полям и ответам — 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 навигация не удалась: обычно прокси недоступен или сайт не отвечает
422 ошибка в запросе: описание в detail

Успех — строго status == "ok".

Таймауты

Прохождение антибота может занять до 2 минут, плюс навигация (timeout) и действия. Таймаут вашего HTTP-клиента к Robotex ставьте не меньше timeout + 120 с + сумма таймаутов действий. Так же настройте reverse proxy перед сервисом.

Пример (Python, httpx)

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