Снимок элемента картинки на текущей странице (без повторной загрузки 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>
robotex — Python-клиент Robotex API
Обвязка над HTTP API Robotex (docs/API.md): задачи v2 через очередь, синхронный /v1/solve, сессии с удержанием окна
браузера. Синхронный Robotex и асинхронный AsyncRobotex с одинаковым интерфейсом. Зависимость одна — httpx.
Установка
pip install "robotex @ git+https://git.sokolab.xyz/s0k0l/robotex.git@v0.1.0#subdirectory=client"
или из wheel (make client-build в репозитории кладёт его в client/dist/):
pip install robotex-0.1.0-py3-none-any.whl
Быстрый старт
from robotex import Robotex, Action
with Robotex("https://robotex.example.com", api_key="rbx_...") as rx:
result = rx.run(
"https://example.com",
[Action.wait_for("h1"), Action.inner_text("h1")],
html=True,
).raise_for_status()
print(result.results[0], result.user_agent, result.cookie("cf_clearance"))
base_url и api_key можно не передавать — тогда берутся из ROBOTEX_URL и ROBOTEX_API_KEY.
Остальные параметры запроса передаются именованными аргументами как есть: proxy, cookies, html, screenshot,
timeout, captcha, captcha_type, disable_resources, blocked_domains, retries, wait, google_search и т. д.
Методы
| Метод | Что делает |
|---|---|
run(url, actions, **opts) |
задача v2: поставить в очередь и дождаться результата (wait= секунд, по умолчанию 600) |
submit(url, actions, **opts) |
только поставить; вернёт TaskRef с .status() и .wait() |
solve(url, actions, **opts) |
синхронный /v1/solve; NoSlotsError, если свободных браузеров нет |
session(session_id=None, idle_timeout=5) |
сессия: один профиль и окно, которое держится idle_timeout минут |
close_session(session_id) |
закрыть окно сессии и освободить слот |
health() |
слоты и воркеры |
Вызовы без сессии каждый раз получают новый профиль браузера.
Сессии и удержание окна
with rx.session(idle_timeout=10) as s:
s.run("https://site/login", [
Action.typing("#login", "me"),
Action.typing("#password", "secret"),
Action.submit("button[type=submit]"),
]).raise_for_status()
# без url — на текущей странице того же окна, без повторного запуска Chrome и антибота
balance = s.run(actions=[Action.inner_text(".balance")]).results[0]
saved = s.id
# на выходе из with окно закрывается (слот освобождается через секунду-две), профиль остаётся
again = rx.session(saved).run("https://site/account") # продолжить сессию позже
Асинхронно
import asyncio
from robotex import AsyncRobotex, Action
async def main():
async with AsyncRobotex() as rx:
results = await asyncio.gather(*(rx.run(url, [Action.inner_text("h1")]) for url in urls))
async with rx.session(idle_timeout=5) as s:
await s.run("https://example.com")
Результат и ошибки
Result: status (ok/err), code, error, user_agent, cookies, actions (ActionResult), results
(тексты действий по порядку), html, screenshot (base64, screenshot_bytes()), duration_ms, session_id.
Ошибка выполнения (challenge_failed, action_failed, request_failed) — это результат со status="err", исключения
нет; raise_for_status() превращает её в ResultError (в e.result всё собранное: cookies, html, скриншот).
Исключения (все от RobotexError):
| Исключение | Когда |
|---|---|
AuthError |
401 — ключ не передан, неверен или отозван |
InvalidRequest |
422 — неверное действие, нет url без открытого окна и т. п. |
NoSlotsError |
503 у solve(); retry_after — через сколько секунд повторить |
NotFound |
404 — задача истекла или из другой сессии |
TaskFailed |
задача failed/cancelled (например, упал воркер) |
TaskTimeout |
wait истёк; задача продолжает выполняться, TaskRef.wait() можно повторить |
ResultError |
raise_for_status() на результате со status="err" |
Действия
Action.fill, typing, click, submit, mouse_move, delay(seconds), wait_for(selector, timeout=None),
inner_html, inner_text, captcha(selector, kind), captcha_image(selector, target, **hints), pass_challenge(). Вместо Action можно передать dict в формате
API: {"type": "click", "selector": "#a"}.
Капча-картинка («код с картинки»): снимок элемента уходит в 2captcha, ответ набирается в поле и возвращается
в result. Подсказки: min_len, max_len, numeric (1 — цифры, 2 — буквы), lang, case_sensitive.
res = s.run(actions=[
Action.captcha_image("#popup-signup img.captcha", '#popup-signup input[name="captcha"]', min_len=4),
Action.submit("#popup-signup button[type=submit]"),
], captcha="<ключ 2captcha>")
code = res.results[0]