Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Когда я разбирался с тонкими моментами организации безопасности при арбитраже трафика, как это часто бывает, просто рабочий проходной скрипт, который я просто написал для тестирования, превратился в отдельный проект. Пока раскручивал тему, вырисовались сразу два самостоятельных скрипта; выкладывать всё одним файлом не стал, решил отдельно расписать что получилось.

Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Разбирая механику арбитража, я собрал автоматический конструктор профилей для антидетект‑браузера через API. Звучит сложно, но на деле очень просто.

Итак, для начинающих (и "продвинутых начинающих") арбитражников, которые хотят работать "через API like a boss", подготовил два рабочих инструмента:

Скрипт №1 массово генерит профили: с авто‑отпечатками браузера от Octo или с вашими собственными (второй вариант требует дополнительно файл с отпечатками браузера).

Скрипт №2 стартует профили не из GUI антидетект‑браузера, а из терминала, сразу подключаясь к ним Selenium’ом. Сам запуск - это конечно мелочь, но для автоматизированного фарминга аккаунтов это может стать отправной точкой. Про сам фарминг будет отдельный лонгрид, а этот скрипт - хорошая база.

Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Поехали по порядку.

Зачем автоматизировать создание профилей

Работа с десятками и сотнями аккаунтов в антидетект‑браузере - это клики, формы, прокси, куки и повторение, повторение, повторение. UI может быть неплохим, но ручное создание профилей всё равно прожигает часы. Когда у вас 10–15 профилей - можно жить и так (но если скрипт уже готов, почему бы не нажать "Enter"?). Но как только счёт пошёл на сотни, скрипт экономит часы и убирает человеческие ошибки: перепутанная метка, не тот порт у прокси, забытый cookie все это не будет ошибкой, если вы используете скрипт а не рутинные действия.

Контекст мультиаккаунтинга: чем закрываем риски

Антидетект‑браузеры - маст‑хэв для задач, где легко поймать блокировку. Они поднимают изолированные профили с уникальными параметрами окружения, имитируя пул устройств и конфигураций. Параллельно можно вести сотни кабинетов или соц аккаунтов с одной машины. Партнер по связке - резидентные прокси: скрывают реальный IP и минимизируют ограничения. В этом материале я использую API Octo Browser и резидентные прокси 2prx. Почему именно Octo (потому что могу) - у меня есть платный тариф Base с доступом к API; почему именно 2prx (потому что хочу) у меня осталось несколько гигабайтов трафика (отдавать просто так жалко, а тут будет полезно). Получилось в итоге бюджетно и эффективно.

Вы можете использовать другие сервисы (но придется немного допилить скрипт), на свое усмотрение, тут просто показано на примере того, к чему у меня был реальный доступ.

Связка "резидентные проки + антидетект браузер" - золотой стандарт. Прокси маскируют сетевой слой, антик закрывает десятки параметров браузера (Canvas, WebGL, таймзона, системный язык, тип девайса и т.д.), но самое главное - даёт изолированный профиль в глазах целевого сервиса (сайта).

Задача: ускорить массовую генерацию профилей через API. Пример: нужно 100 штук, у каждой - свой фингерпринт и свой прокси. Ручками это полдня. Octo предоставляет открытое API - этим и воспользуемся.

Подготовка: API, входные данные, окружение

API‑ключ. Octo выдаёт токен в десктопном клиенте (это вообще на любителя; мне проще было бы в веб‑кабинете, чтобы не ставить на комп приложение). Токен нужен для авторизации запросов.

Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Прокси. Берём на 2prx с пакетной выдачей. Формируем файл proxies.csv в формате:

type,host,port,login,password HTTP,192.0.2.10,24000,mylogin,mypass Тип обязателен (HTTP/HTTPS или SOCKS5), иначе Octo вернёт 400.

Cookie (опционально). Если хотите сразу импортировать сессию можно подготовить такой файл - cookies.json:

{ "0": [ { "name": "...", "value": "...", "domain": "...", "path": "/", "expiry": 9999999999 } ], "1": [ /* куки для профиля №2 */ ] }

Ключи - это индексы профилей с нуля. Нет файла - шаг пропускается.

Переменные окружения - файл .env рядом со скриптами:

OCTO_API_TOKEN=<токен Octo> PROXY_FILE=proxies.csv COOKIE_FILE=cookies.json PROFILE_COUNT=0 PROFILE_COUNT=0 означает "создать по числу прокси". Если зададите больше, чем прокси, адреса начнут циклически повторяться.

Зависимости:

pip install requests python-dotenv selenium

Исходники я положил в GitHub под названием Octo Browser Profile Creator.

Скрипт №1 - массовое создание профилей через антидетект браузер Octo API

Исходники я положил в GitHub под названием Octo Browser Profile Creator.

Скрипт №1 - массовое создание профилей через антидетект браузер Octo API

Ниже - полностью рабочий код - такой же есть на Гитхабе.

#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Bulk builder for Octo Browser profiles • берет прокси из CSV (auto-dialect) • по желанию подмешивает cookies из JSON • фингерпринт генерится Octo по минимальному шаблону • количество профилей задаётся PROFILE_COUNT (0 = по числу прокси) """ from __future__ import annotations import csv import itertools import json import os import sys import time from pathlib import Path from typing import Any, Dict, List import requests from dotenv import load_dotenv # ─────────────────────────── Константы ─────────────────────────── API_ROOT = "https://app.octobrowser.net/api/v2/automation" TIMEOUT_S = 30 # таймаут HTTP DELAY_S = 0.5 # пауза между запросами FP_STUB = {"os": "win", "screen": "1920x1080"} # остальное Octo подставит сам BASE_DIR = Path(__file__).parent.resolve() load_dotenv() # читаем .env из текущей папки TOKEN = os.getenv("OCTO_API_TOKEN", "") if not TOKEN: sys.exit("🛑 В .env не найден OCTO_API_TOKEN") HEADERS = {"X-Octo-Api-Token": TOKEN} PROXY_FILE = BASE_DIR / os.getenv("PROXY_FILE", "proxies.csv") COOKIE_FILE = BASE_DIR / os.getenv("COOKIE_FILE", "cookies.json") COUNT_RAW = os.getenv("PROFILE_COUNT", "0") try: PROFILE_COUNT = int(COUNT_RAW) except ValueError: sys.exit("🛑 PROFILE_COUNT должен быть числом") # ──────────────────────────── Утилиты ──────────────────────────── def sniff_dialect(path: Path) -> csv.Dialect: sample = path.read_text(encoding="utf-8")[:2048] return csv.Sniffer().sniff(sample, delimiters=",;\t ") def read_proxies(path: Path) -> List[Dict[str, Any]]: if not path.exists(): sys.exit(f"🛑 Не найден файл с прокси: {path}") dialect = sniff_dialect(path) out: List[Dict[str, Any]] = [] with path.open(encoding="utf-8", newline="") as fh: reader = csv.DictReader(fh, dialect=dialect) for row in reader: row = { (k or "").strip(): (v or "").strip() for k, v in row.items() } if not row.get("port"): sys.exit(f"🛑 Пустой порт в строке: {row}") try: row["port"] = int(row["port"]) except Exception: sys.exit(f"🛑 Некорректный порт в строке: {row}") out.append(row) if not out: sys.exit("🛑 В CSV нет валидных прокси") return out def read_cookies(path: Path) -> Dict[str, List[Dict[str, Any]]]: if not path.exists(): return {} try: data = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError as e: sys.exit(f"🛑 Невалидный JSON в cookies: {e}") if not isinstance(data, dict): sys.exit("🛑 cookies.json должен содержать JSON-объект (словарь)") return data def api_post(endpoint: str, payload: Dict[str, Any]) -> Dict[str, Any]: url = f"{API_ROOT}/{endpoint.lstrip('/')}" resp = requests.post(url, json=payload, headers=HEADERS, timeout=TIMEOUT_S) resp.raise_for_status() body = resp.json() if "data" not in body: raise RuntimeError(f"Неожиданный ответ API: {body}") return body["data"] # ─────────────────────────── Точка входа ───────────────────────── def main() -> None: proxies = read_proxies(PROXY_FILE) cookies_map = read_cookies(COOKIE_FILE) total = PROFILE_COUNT if PROFILE_COUNT > 0 else len(proxies) proxy_cycle = itertools.cycle(proxies) for idx in range(1, total + 1): proxy = next(proxy_cycle) title = f"AutoProfile_{idx:03d}" cookies = cookies_map.get(str(idx - 1)) payload = { "title": title, "proxy": proxy, "fingerprint": FP_STUB } if cookies: payload["cookies"] = cookies try: data = api_post("profiles", payload) print(f"✅ Создан профиль #{idx}: UUID {data.get('uuid')}") except requests.HTTPError as e: print(f"❌ HTTP ошибка для профиля #{idx}: {e}") if e.response is not None: print(" Ответ сервера:", e.response.text) except Exception as e: print(f"❌ Сбой на профиле #{idx}: {e}") time.sleep(DELAY_S) if __name__ == "__main__": main()

Что делает этот скрипт - по шагам

Конфигурация. Тянет токен и имена файлов из .env. Нет токена - мгновенный выход с ошибкой.

CSV с прокси. Автоопределение разделителя (csv.Sniffer), ранний стоп при мусоре в данных.

JSON с cookie (если есть). Требуется объект‑словарь: ключ - индекс профиля (строка), значение - список cookie‑словарей.

Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Повтор прокси по кругу. itertools.cycle() позволяет создавать профилей больше, чем уникальных прокси. Риски думаю понятны: один IP на несколько профилей может палиться, поэтому рекомендую использовать уникальные прокси на каждый новый профиль при серьезных проектах.

Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Отпечатки браузера. Передаем только обязательное: ОС (win/mac) и экран. Остальное (~50 полей) Octo подставляет сам (можно дописать свои параметры, при желании).

POST в API. https://app.octobrowser.net/api/v2/automation/profiles, заголовок X-Octo-Api-Token, аккуратная обработка ошибок и логирование UUID.

Вывод. Консоль показывает прогресс:

✅ Создан профиль #001: UUID 123e4567-e89b-12d3-a456-426614174000 ...

После прогона профили будут видны в списке Octo: каждый привязан к своему прокси, с валидным отпечатком; если подставляли cookie - они также будут добавлены.

Автоматизация профилей в антидетект‑браузере через API: заметки из практики

Скрипт №2 - старт профилей из терминала + Selenium и проверка IP

Этот мини‑инструмент вытягивает OCTO_LOCAL_PORT и OCTO_API_TOKEN из .env, принимает на вход один или несколько UUID, запускает каждый профиль через локальный API Octo, коннектится к нему по Chrome DevTools и открывает httpbin.org/ip для проверки исходящего адреса. База для старта автоматического фарминга.

#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ CLI-стартер для профилей Octo Browser: • запускает профиль по UUID через Local API • подключается к уже запущенному браузеру через Selenium (debuggerAddress) • заходит на httpbin.org/ip и печатает внешний IP """ import argparse import os import sys import requests from dotenv import load_dotenv from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait # ───────────────────── Конфигурация окружения ───────────────────── load_dotenv() LOCAL_PORT = os.getenv("OCTO_LOCAL_PORT", "58888") API_TOKEN = os.getenv("OCTO_API_TOKEN") if not API_TOKEN: sys.exit("⛔️ В .env отсутствует OCTO_API_TOKEN") BASE_URL = f"http://127.0.0.1:{LOCAL_PORT}" HEADERS = {"X-Octo-Api-Token": API_TOKEN, "Content-Type": "application/json"} REQ_TIMEOUT = 15 # ───────────────────── Функции управления профилем ───────────────── def start_profile(uuid: str, headless: bool = False) -> int: """ Стартует профиль и возвращает порт удаленной отладки (Chrome DevTools). """ url = f"{BASE_URL}/api/profiles/start" payload = {"uuid": uuid, "headless": headless, "debug_port": True} print(f"▶️ Запускаю профиль {uuid}...") resp = requests.post(url, json=payload, headers=HEADERS, timeout=REQ_TIMEOUT) if resp.status_code == 400: print(f"⛔️ [400 Bad Request] Профиль не стартовал. Ответ API: {resp.text}") resp.raise_for_status() debug_port = resp.json().get("debug_port") if not debug_port: raise ValueError("API не вернул debug_port") print(f"✅ Профиль {uuid} поднят, debug-порт: {debug_port}") return int(debug_port) def attach_via_selenium(port: int) -> webdriver.Chrome: """ Подключается к уже запущенному Octo-профилю с помощью Selenium. """ options = Options() options.add_experimental_option("debuggerAddress", f"127.0.0.1:{port}") # chromedriver должен быть в PATH driver = webdriver.Chrome(options=options) return driver def fetch_external_ip(driver: webdriver.Chrome) -> str: """ Открывает httpbin.org/ip и возвращает JSON-строку с IP. """ driver.get("https://httpbin.org/ip") pre = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.TAG_NAME, "pre"))) return pre.text.strip() # ───────────────────────────── main ──────────────────────────────── def main(uuids: list[str]) -> None: for uid in uuids: driver = None try: port = start_profile(uid) driver = attach_via_selenium(port) ip_info = fetch_external_ip(driver) print(f" IP-ответ для {uid}: {ip_info}\n") except requests.RequestException as e: print(f"⛔️ Ошибка API для {uid}: {e}\n") except Exception as e: print(f"⛔️ Непредвиденная ошибка для {uid}: {e}\n") finally: if driver: driver.quit() if __name__ == "__main__": parser = argparse.ArgumentParser(description="Старт профилей Octo и проверка их внешнего IP.") parser.add_argument("profile_uuids", nargs="+", metavar="UUID", help="Один или несколько UUID профилей") args = parser.parse_args() main(args.profile_uuids)

Как это работает - пошагово

Настройки. load_dotenv() подтягивает OCTO_LOCAL_PORT (по умолчанию 58888) и OCTO_API_TOKEN. Сборка базового URL и заголовков.

Аргументы CLI. Вы передаёте один или несколько UUID:

python check_ip.py uuid1 uuid2 uuid3

Старт профиля. POST http://127.0.0.1:<PORT>/api/profiles/start с телом:

{ "uuid": "<UID>", "headless": false, "debug_port": true }

При 400 скрипт печатает ответ API, дальше - raise_for_status() для остальных ошибок. Возвращаем debug_port.

Подключение к профилю. Selenium цепляется к уже запущенному браузеру через options.add_experimental_option("debuggerAddress", "127.0.0.1:<port>"). Дальше можно управлять страницей как обычно.

Проверка IP. Переход на https://httpbin.org/ip, ожидание <pre>, чтение JSON‑ответа с исходящим адресом.

Аккуратное закрытие. finally гарантирует driver.quit().

Куда развивать дальше

Что дальше, можно ли допилить? Я подкину несколько путей развития, на мой вкус. Но делать я этого, в ближайшее время конечно не буду.

  • Автоматически вешать теги, складывать UUID в БД, запускать профили батчами.

  • Прогонять сценарии сразу после старта профиля (сеансы, прогрев, сбор метрик).

  • Завести очередь задач на запуск/остановку профилей и мониторинг статусов API.

Не переоптимизируйте: больше автоматики - больше точек отказа. Баланс важен.

Быстрая проверка логики (чтобы не словить банальные ошибки)

В proxies.csv тип обязателен (HTTP/HTTPS/SOCKS5), port - число.

В cookies.json корнем должен быть объект, ключи - строки индексов ("0", "1", …).

PROFILE_COUNT=0 - создаст профилей ровно по числу строк в CSV; больше - включится цикл по прокси.

Минимальный фингерпринт (os, screen) - норм; остальное Octo достраивает сам.

Начать дискуссию