Пакет в client/ (зависимость — httpx), установка через pip из git или wheel (make client-build). Тесты против api и воркера из harness. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9.3 KiB
Использование API: практики для клиентов
Как встроить Robotex в свой парсер или бота, чтобы антибот проходился стабильно, а полученные cookies работали. Справочник по полям и ответам — API.md.
Основной сценарий
Robotex проходит антибот в настоящем браузере и отдаёт то, с чем клиент дальше ходит на сайт своим HTTP-клиентом:
POST /v1/solveсurl, своимproxyиcaptcha_type.- Из ответа взять
cookies(среди нихcf_clearance) иuser_agent. - Дальше ходить на сайт своим HTTP-клиентом:
- с тем же User-Agent, что пришёл в ответе;
- через тот же прокси (тот же exit-IP);
- с полученными cookies.
- Когда сайт снова отвечает челленджем (обычно
403и страница «Just a moment…») или истекаетcf_clearance(полеexpiresу cookie) — повторить/v1/solveв той же сессии (cookiesession_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}с той же cookiesession_id. Подходит, когда соединение нельзя держать долго, и для многошаговых сценариев.
Многошаговый сценарий (войти, затем несколько раз что-то сделать на сайте) — с удержанием окна:
POST /v2/tasksсurlиbrowser_idle_timeout: окно останется открытым на это число минут простоя.- Следующие задачи с той же cookie
session_idи тем жеproxyвыполняются в открытом окне: без запуска Chrome и повторного антибота. Безurl— на текущей странице. - В конце —
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): задачи, сессии с удержанием окна,
ожидание результата, ошибки в виде исключений, sync и async.
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)
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