Снимок элемента картинки на текущей странице (без повторной загрузки src) уходит в 2captcha как обычная капча (method=base64), ответ набирается в поле target как у typing и возвращается в result действия. Необязательные подсказки: min_len, max_len, numeric, lang, case_sensitive. Клиент: Action.captcha_image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
245 lines
18 KiB
Markdown
245 lines
18 KiB
Markdown
# API
|
||
|
||
Как встраивать API в свой клиент (UA, прокси, сессии, повторы) — [Usage.md](Usage.md).
|
||
|
||
Задача открывает страницу в Chrome (через прокси клиента, если он задан), при необходимости проходит антибот
|
||
(Cloudflare Turnstile, reCAPTCHA), выполняет список действий и возвращает cookies, результаты действий и, по запросу,
|
||
HTML и скриншот. Задачи выполняет сервис worker из очереди; API работает в двух режимах:
|
||
|
||
* `POST /v1/solve` — синхронно: ответ приходит, когда задача выполнена;
|
||
* `POST /v2/tasks` + `GET /v2/tasks/{id}` — асинхронно, с возможностью держать окно браузера открытым между
|
||
задачами (см. [API v2](#api-v2)).
|
||
|
||
`GET /health` (и `GET /`) — `{"status": "ok", "browser_slots": N, "free_slots": M, "workers": [...]}`: слоты всех
|
||
живых воркеров; `workers` — по каждому `worker_id`, `browser_slots`, `free_slots`, `windows` (открытые окна),
|
||
`running` (выполняемые задачи).
|
||
|
||
## Авторизация
|
||
|
||
Все эндпоинты, кроме `/health`, требуют ключ клиента в заголовке `X-API-Key`. Без ключа, с неверным или отозванным
|
||
ключом — `401`. Ключи выпускает администратор (`make key-create name=<клиент>`, см. [Deployment.md](Deployment.md#клиенты-и-ключи)).
|
||
|
||
Задача и окно сессии видны только клиенту, который их создал: с другим ключом `GET /v2/tasks/{id}` даёт `404`, а
|
||
чужое открытое окно не используется.
|
||
|
||
## Запрос
|
||
|
||
```http
|
||
POST /v1/solve
|
||
Content-Type: application/json
|
||
X-API-Key: <key>
|
||
Cookie: session_id=<id> # необязательно, см. «Сессии»
|
||
|
||
{
|
||
"url": "https://freelancehunt.com/projects",
|
||
"proxy": "http://user:pass@host:port",
|
||
"captcha_type": "cloudflare",
|
||
"actions": [
|
||
{"type": "wait_for", "selector": "a[href*=\"/project/\"]", "timeout": 60}
|
||
],
|
||
"html": true,
|
||
"timeout": 120
|
||
}
|
||
```
|
||
|
||
| Поле | Тип | По умолчанию | Описание |
|
||
|---|---|---|---|
|
||
| `url` | string | — | **обязательно**. Страница, которую открываем |
|
||
| `proxy` | string | `null` | `scheme://[user:pass@]host:port`, схема `http`, `https` или `socks5` (Chrome не поддерживает socks5 с логином/паролем). По exit-IP прокси выставляются locale и timezone браузера; без прокси — настройки браузера по умолчанию |
|
||
| `actions` | array | — | **обязательно**, может быть пустым. Действия, см. ниже |
|
||
| `captcha_type` | string | `null` | провайдер антибота: `cloudflare` или `recaptcha`. Без него шаг антибота пропускается — для сайтов за Cloudflare обязательно |
|
||
| `captcha` | string | `null` | ключ сервиса распознавания (2captcha), если антибот не проходится без решения капчи |
|
||
| `captcha_detect_locator` | string | `null` | локатор, по которому определяется, что на странице антибот |
|
||
| `captcha_success_locator` | string | `null` | локатор, по которому определяется, что антибот пройден |
|
||
| `html` | bool | `false` | вернуть HTML страницы после выполнения действий |
|
||
| `screenshot` | bool | `false` | вернуть скриншот видимой области (PNG, base64) после действий — в том числе при ошибке действия или антибота |
|
||
| `timeout` | int, с | `60` | таймаут навигации |
|
||
| `cookies` | array | `null` | cookies для установки до навигации, формат Playwright: `{name, value, domain, path, expires, httpOnly, secure, sameSite}` или `{name, value, url}`. **Заменяют** cookies профиля: перед установкой все cookies профиля удаляются (`[]` — просто очистить). Без поля cookies профиля сохраняются |
|
||
| `disable_resources` | bool | `false` | не грузить картинки, шрифты, медиа |
|
||
| `blocked_domains` | array | `null` | домены, запросы к которым блокируются |
|
||
| `retries` | int | `1` | попыток навигации |
|
||
| `retry_delay` | float, с | `1.0` | пауза между попытками |
|
||
| `wait` | int, мс | `0` | пауза после загрузки страницы |
|
||
| `google_search` | bool | `false` | выставить Referer как переход из поиска Google |
|
||
|
||
### Действия
|
||
|
||
Выполняются по порядку после прохождения антибота. Если антибот не пройден, действия не выполняются
|
||
(`code: challenge_failed`). На первой ошибке действия выполнение останавливается.
|
||
|
||
Обязательные поля проверяются до запуска браузера: неизвестный `type`, нет `selector`/`value`, `captcha` без ключа
|
||
в запросе, неверный `proxy` — ответ `422` с описанием в `detail` (стандартный формат FastAPI).
|
||
|
||
| `type` | Поля | Что делает |
|
||
|---|---|---|
|
||
| `wait_for` | `selector`, `timeout` (1–7200 с, по умолчанию 30) | ждёт появления элемента; при таймауте — ошибка действия |
|
||
| `click` | `selector` | клик по элементу |
|
||
| `submit` | `selector` | клик и ожидание навигации |
|
||
| `fill` | `selector`, `value` | мгновенная вставка значения в поле |
|
||
| `typing` | `selector`, `value` | ввод как у человека: подвод курсора, клик, очистка поля, посимвольный набор с паузами 50–200 мс; медленнее `fill`, но не выдаёт автоматизацию |
|
||
| `inner_text` | `selector` | возвращает текст элемента (HTML без тегов) в `result` |
|
||
| `mouse_move` | `selector` | наведение курсора на элемент; нет элемента — ошибка `selector not found` |
|
||
| `delay` | `value` (секунды строкой, 0–600) | пауза |
|
||
| `inner_html` | `selector` | возвращает HTML элемента в `result`; нет элемента — ошибка `selector not found` |
|
||
| `captcha` | `selector`, `value` (тип: `cloudflare`, `recaptcha`, `hcaptcha`) | решить капчу внутри страницы через 2captcha, нужен `captcha` в запросе |
|
||
| `captcha` (`value: "image"`) | `selector` (картинка), `target` (поле ввода); подсказки необязательны: `min_len`, `max_len` (1–20), `numeric`, `lang`, `case_sensitive` | капча-картинка «код с картинки»: скриншот элемента `selector` на текущей странице (без повторной загрузки, код не меняется) уходит в 2captcha, ответ набирается в `target` как у `typing` и возвращается в `result`. Не решена или таймаут 2captcha — ошибка действия. `numeric`: `1` — только цифры, `2` — только буквы, `3` — цифры или буквы, `4` — и цифры, и буквы |
|
||
| `pass_challenge` | `selector` (локатор успеха, необязательно) | повторно пройти антибот, например после перехода на другую страницу; если не пройден — ошибка действия |
|
||
|
||
Капча-картинка в форме регистрации — решить, затем отправить форму. Если сайт отклонил код и показал новую картинку,
|
||
повторите задачу v2 в открытом окне сессии (`browser_idle_timeout`): страница не перезагружается.
|
||
|
||
```json
|
||
{"captcha": "<ключ 2captcha>", "browser_idle_timeout": 5, "actions": [
|
||
{"type": "captcha", "selector": "#popup-signup img.captcha", "value": "image",
|
||
"target": "#popup-signup input[name=\"captcha\"]", "min_len": 4, "max_len": 6},
|
||
{"type": "submit", "selector": "#popup-signup button[type=submit]"}
|
||
]}
|
||
```
|
||
|
||
## Ответ
|
||
|
||
Формат один для всех случаев: `status` — `"ok"` или `"err"`, при ошибке — `code` и `error`. Поля без значения не
|
||
возвращаются.
|
||
|
||
Успех (антибот пройден, действия выполнены), HTTP `200`:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) ... Chrome/155.0.0.0 Safari/537.36",
|
||
"cookies": [{"name": "cf_clearance", "value": "...", "domain": ".freelancehunt.com", "...": "..."}],
|
||
"actions": [
|
||
{"type": "wait_for", "ok": true, "duration_ms": 14}
|
||
],
|
||
"html": "<head>...</head><body>...</body>",
|
||
"duration_ms": 6405
|
||
}
|
||
```
|
||
|
||
| Поле | Описание |
|
||
|---|---|
|
||
| `status` | `"ok"` — антибот пройден и все действия выполнены, иначе `"err"` |
|
||
| `code` | при `"err"`: причина, см. таблицу ниже |
|
||
| `error` | при `"err"`: текст ошибки; для упавшего действия — `<type>: <текст>` |
|
||
| `user_agent` | родной UA браузера, с которым проходили антибот. Используйте его в своём HTTP-клиенте вместе с `cf_clearance` (см. [Usage.md](Usage.md)) |
|
||
| `cookies` | все cookies контекста |
|
||
| `actions` | по элементу на выполненное действие: `type`, `ok`, `result` (результат или текст ошибки), `duration_ms` |
|
||
| `html` | только при `"html": true` |
|
||
| `screenshot` | только при `"screenshot": true`: PNG в base64 |
|
||
| `duration_ms` | общее время обработки запроса |
|
||
|
||
| `code` | HTTP | Когда | Что ещё есть в ответе |
|
||
|---|---|---|---|
|
||
| `no_slots` | `503` + `Retry-After` | только `/v1/solve`: все слоты браузеров заняты, или воркер не взял задачу за `V1_QUEUE_WAIT` секунд (задача отменяется) | — |
|
||
| `request_failed` | `200` | ошибка до действий: навигация, прокси, неизвестный `captcha_type` | `actions: []` |
|
||
| `challenge_failed` | `200` | антибот не пройден за отведённое время, действия не выполнялись | cookies, html, screenshot |
|
||
| `action_failed` | `200` | упало действие, выполнение остановлено на нём | cookies, html, screenshot, `actions` до упавшего включительно |
|
||
|
||
Примеры:
|
||
|
||
```json
|
||
{"status": "err", "code": "action_failed", "error": "inner_html: selector not found: #missing",
|
||
"actions": [{"type": "inner_html", "ok": false, "result": "selector not found: #missing", "duration_ms": 26}], "...": "..."}
|
||
```
|
||
|
||
```json
|
||
{"status": "err", "code": "challenge_failed", "error": "cloudflare not passed: timeout (stage: checkbox)", "actions": [], "...": "..."}
|
||
```
|
||
|
||
```json
|
||
{"status": "err", "code": "no_slots", "error": "No slots available"}
|
||
```
|
||
|
||
## Сессии
|
||
|
||
Ответ ставит cookie `session_id` (UUID). Если передать её в следующем запросе, браузер откроется с тем же профилем
|
||
(`extra/user_data_dir/<session_id>`): cookies, localStorage и отпечаток сохраняются, повторно проходить Cloudflare
|
||
обычно не нужно. Без cookie или с некорректным значением (не UUID) запрос получает новый профиль.
|
||
|
||
Сессии изолированы друг от друга: у каждой свой профиль Chrome — cookies, localStorage, кэш.
|
||
|
||
Отпечаток профиля генерируется при первом запросе сессии и дальше не меняется (`robotex_session.json` в каталоге
|
||
профиля):
|
||
|
||
* размер экрана и `devicePixelRatio` десктопного Chrome на Linux из реального распределения (browserforge) —
|
||
нативной эмуляцией Chrome;
|
||
* шум canvas: свой seed у каждого профиля, поэтому хеш canvas у профилей разный, а внутри профиля стабильный.
|
||
Меняется младший бит у редких непрозрачных пикселей при чтении canvas (`toDataURL`, `toBlob`, `getImageData`);
|
||
пустой canvas не меняется.
|
||
|
||
Остальное — WebGL, `hardwareConcurrency`, шрифты, UA — не подменяется: это родные значения Chrome, за маскировку
|
||
автоматизации отвечает Patchright. Полная JS-подмена отпечатка ломала прохождение Cloudflare.
|
||
|
||
User-Agent и размер окна запросом не задаются — это родные значения браузера (почему — [Usage.md](Usage.md#что-задаёт-сервис-а-не-клиент)).
|
||
Поле `user_agent` в запросе даёт `422`.
|
||
|
||
Задачи одной сессии выполняются по очереди: профиль Chrome нельзя открыть дважды. Вторая задача ждёт, пока
|
||
закончится первая.
|
||
|
||
## API v2
|
||
|
||
Асинхронный режим: задача ставится в очередь, клиент опрашивает её по ссылке. Тело задачи — как у `/v1/solve`, плюс:
|
||
|
||
| Поле | Тип | По умолчанию | Описание |
|
||
|---|---|---|---|
|
||
| `browser_idle_timeout` | int, мин | `0` | держать окно браузера открытым после задачи столько минут простоя, `0`–`120`. `0` — закрыть сразу, как в v1 |
|
||
| `url` | string | `null` | необязателен, если окно сессии открыто: тогда действия выполняются на текущей странице, без навигации |
|
||
|
||
### POST /v2/tasks
|
||
|
||
```http
|
||
POST /v2/tasks
|
||
Content-Type: application/json
|
||
Cookie: session_id=<id> # необязательно
|
||
|
||
{"url": "https://freelancehunt.com/projects", "captcha_type": "cloudflare", "actions": [], "browser_idle_timeout": 10}
|
||
```
|
||
|
||
Ответ `202` и cookie `session_id`:
|
||
|
||
```json
|
||
{"task_id": "0b9c…", "status": "queued", "url": "/v2/tasks/0b9c…"}
|
||
```
|
||
|
||
Без `url` и без открытого окна сессии — `422`.
|
||
|
||
### GET /v2/tasks/{id}
|
||
|
||
Только с cookie `session_id` той сессии, в которой задача создана; иначе, а также для несуществующей или истёкшей
|
||
задачи — `404`.
|
||
|
||
```json
|
||
{"task_id": "0b9c…", "status": "done", "result": {"status": "ok", "user_agent": "…", "cookies": […], "actions": […], "duration_ms": 7120}}
|
||
```
|
||
|
||
| `status` | Значение |
|
||
|---|---|
|
||
| `queued` | ждёт свободного слота браузера |
|
||
| `running` | выполняется |
|
||
| `done` | готово, ответ в `result` — в том же формате, что ответ `/v1/solve` (со своим `status` `ok`/`err`) |
|
||
| `failed` | задачу не удалось выполнить (например, воркер упал дважды), причина в `error` |
|
||
|
||
Результат хранится `RESULT_TTL_HOURS` часов (по умолчанию 24), затем `404`. Опрашивать раз в 0,5–2 секунды.
|
||
|
||
### Окно браузера между задачами
|
||
|
||
С `browser_idle_timeout > 0` Chrome сессии не закрывается после задачи. Следующая задача с той же cookie
|
||
`session_id` выполняется в этом окне: без запуска Chrome и обычно без повторного антибота, а без `url` — прямо на
|
||
текущей странице (например, продолжить заполнение формы). Каждая задача в окно заново отсчитывает простой — по
|
||
своему `browser_idle_timeout`; задача с `0` закрывает окно после себя.
|
||
|
||
* Открытое окно занимает слот браузера, пока не закроется. Не держите окна дольше, чем нужно.
|
||
* Задача с другим `proxy`, чем у открытого окна, перезапускает Chrome (прокси задаётся при запуске).
|
||
* `cookies` в задаче заменяют cookies открытого окна, как и при запуске.
|
||
* Если задача в окне упала с `request_failed`, окно закрывается; следующая задача откроет его заново из профиля.
|
||
|
||
### DELETE /v2/sessions/current
|
||
|
||
Закрыть окно сессии (по cookie `session_id`) досрочно и освободить слот. Если в окне идёт задача — сразу после неё.
|
||
Ответ `{"closed": true}`, или `{"closed": false}`, если окна нет.
|
||
|
||
## Производительность
|
||
|
||
Каждое окно — отдельный Chrome: около 0,6 ГБ памяти и до 3–4 ядер на пике при прохождении Cloudflare.
|
||
Замеры и рекомендации по лимитам — в [Deployment.md](Deployment.md#нагрузка).
|