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
+11
-11
@@ -1,5 +1,7 @@
|
||||
# API
|
||||
|
||||
Как встраивать API в свой клиент (UA, прокси, сессии, повторы) — [Usage.md](Usage.md).
|
||||
|
||||
Основной эндпоинт: `POST /v1/solve`. Открывает страницу в Chrome (через прокси клиента, если он задан), при необходимости проходит
|
||||
антибот (Cloudflare Turnstile, reCAPTCHA), выполняет список действий и возвращает cookies, результаты действий и,
|
||||
по запросу, HTML и скриншот.
|
||||
@@ -40,7 +42,6 @@ Cookie: session_id=<id> # необязательно, см. «Сесси
|
||||
| `html` | bool | `false` | вернуть HTML страницы после выполнения действий |
|
||||
| `screenshot` | bool | `false` | вернуть скриншот видимой области (PNG, base64) после действий — в том числе при ошибке действия или антибота |
|
||||
| `timeout` | int, с | `60` | таймаут навигации |
|
||||
| `user_agent` | string | `null` | свой User-Agent. Задаётся профилю **один раз** — в первом запросе сессии; в следующих запросах этой сессии поле игнорируется. Без него профилю генерируется отпечаток (см. «Сессии») |
|
||||
| `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` | домены, запросы к которым блокируются |
|
||||
@@ -94,7 +95,7 @@ Cookie: session_id=<id> # необязательно, см. «Сесси
|
||||
| `status` | `"ok"` — антибот пройден и все действия выполнены, иначе `"err"` |
|
||||
| `code` | при `"err"`: причина, см. таблицу ниже |
|
||||
| `error` | при `"err"`: текст ошибки; для упавшего действия — `<type>: <текст>` |
|
||||
| `user_agent` | UA браузера, с которым проходили антибот (нужен клиенту вместе с `cf_clearance`) |
|
||||
| `user_agent` | родной UA браузера, с которым проходили антибот. Используйте его в своём HTTP-клиенте вместе с `cf_clearance` (см. [Usage.md](Usage.md)) |
|
||||
| `cookies` | все cookies контекста |
|
||||
| `actions` | по элементу на выполненное действие: `type`, `ok`, `result` (результат или текст ошибки), `duration_ms` |
|
||||
| `html` | только при `"html": true` |
|
||||
@@ -126,18 +127,17 @@ Cookie: session_id=<id> # необязательно, см. «Сесси
|
||||
## Сессии
|
||||
|
||||
Ответ ставит cookie `session_id` (UUID). Если передать её в следующем запросе, браузер откроется с тем же профилем
|
||||
(`extra/user_data_dir/<session_id>`): cookies, localStorage, `user_agent` и отпечаток сохраняются, повторно проходить Cloudflare
|
||||
(`extra/user_data_dir/<session_id>`): cookies, localStorage и отпечаток сохраняются, повторно проходить Cloudflare
|
||||
обычно не нужно. Без cookie или с некорректным значением (не UUID) запрос получает новый профиль.
|
||||
|
||||
User-Agent и отпечаток профиля задаются при первом запросе сессии и дальше не меняются (`robotex_session.json` в
|
||||
каталоге профиля):
|
||||
Отпечаток профиля генерируется при первом запросе сессии и дальше не меняется (`robotex_session.json` в каталоге
|
||||
профиля): размер экрана и `devicePixelRatio` десктопного Chrome на Linux из реального распределения (browserforge).
|
||||
Применяется нативной эмуляцией Chrome, без JS-подмены (она ломала прохождение Cloudflare); DPR влияет и на хеш
|
||||
canvas. Ограничение: canvas, WebGL и `hardwareConcurrency` у всех профилей одинаковые, поэтому примерно половина
|
||||
профилей совпадает по отпечатку.
|
||||
|
||||
- передан `user_agent` — профилю записывается он;
|
||||
- не передан — генерируется отпечаток десктопного Chrome на Linux (browserforge): UA с версией установленного Chrome,
|
||||
размер экрана и `devicePixelRatio` из реального распределения. Применяется нативной эмуляцией Chrome, без
|
||||
JS-подмены (она ломала прохождение Cloudflare). DPR влияет и на хеш canvas. Ограничение: canvas, WebGL и
|
||||
`hardwareConcurrency` у всех профилей одинаковые, поэтому примерно половина профилей совпадает по отпечатку
|
||||
(`make fp-check` показывает долю различий).
|
||||
User-Agent и размер окна запросом не задаются — это родные значения браузера (почему — [Usage.md](Usage.md#что-задаёт-сервис-а-не-клиент)).
|
||||
Поле `user_agent` в запросе даёт `422`.
|
||||
|
||||
Один профиль нельзя использовать в двух запросах одновременно: Chrome заблокирует каталог профиля.
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
антибот (Cloudflare Turnstile, reCAPTCHA), выполняет действия и возвращает cookies (включая `cf_clearance`),
|
||||
User-Agent и, по запросу, HTML. С этими cookies и UA клиент продолжает парсинг своим HTTP-клиентом.
|
||||
|
||||
Описание запроса и ответа — [API.md](API.md), развёртывание — [Deployment.md](Deployment.md).
|
||||
Как пользоваться API — [Usage.md](Usage.md), описание запроса и ответа — [API.md](API.md), развёртывание — [Deployment.md](Deployment.md).
|
||||
+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