Регистрация и прогрев 109 аккаунтов на GitHub и Reddit: восемь дней логов
Зарегистрироваться - простая половина задачи. Аккаунт чего-то стоит только если через неделю им всё ещё можно пользоваться, с той же личности, без повторного входа и без цикла подтверждений. Поэтому единственное измерение, которое имеет смысл, длится днями, а не один запуск.
Вот такое измерение: обвязка на нашем SDK, которая регистрирует аккаунты на GitHub, Reddit и Hacker News, а потом раз в день-два открывает каждый из них заново и работает им так, как это делал бы человек. Восемь дней, с 2026-08-05 по 2026-08-13. Каждый запуск шёл через AntiDetectBrowser.launch(), каждое действие писалось в Postgres, а пакетные прогоны оставили на диске 4 125 строк логов. Цифры ниже взяты из этой базы и из этих логов, а не из демо.
Итоги восьми дней
на 109 регистраций
за 32 сессии прогрева
на регистрации
совпали с заданным
| Аккаунтов создано обвязкой | 109 |
| Живы на конец периода | 89 (81,7%) |
| Записанных действий | 1 989 |
| Открыто страниц репозиториев при прогреве | 661 |
| Сессий прогрева начато / завершено | 146 / 141 (96,6%) |
Выборка перекошена намеренно. На GitHub пришлось 103 аккаунта, потому что это самая сложная цель: форма регистрации за оверлейной антибот-проверкой, код на почту и пространство имён, где почти все короткие ники уже заняты. На Reddit - 5, на Hacker News - 1: этого хватает, чтобы убедиться, что схема работает, и совершенно недостаточно, чтобы называть процент.
Ноль проверок - и что это на самом деле значит
grep -ci 'arkose\|hcaptcha\|recaptcha\|captcha' по всем шести пакетным логам возвращает 0. Это и есть заявление, и стоит точно оговорить, чего оно не включает.
Коды подтверждения на почту никуда не делись. GitHub просил их практически на каждой регистрации, и обвязка забирала код из почтового ящика и вводила его. Это проверка владения, а не проверка на человека, и никакая работа с отпечатком её не отменяет.
Чего не было - так это второго типа: пазла, ползунка, «выберите все картинки с автобусом». У GitHub на /signup стоит оверлейная антибот-проверка, и она ни разу не переросла в видимый челлендж. Эскалация случается, когда браузер выглядит неправильно: UA говорит Android, а maxTouchPoints равен 0; часовой пояс - Шанхай, а выходной IP - Орегон; canvas и WebGL дают хеш, который сайт видел на этой неделе четыре тысячи раз. Ничего из этого не сработало на 109 регистрациях, потому что ни одного такого противоречия не было.
Обвязка к тому же устроена так, чтобы на проверке останавливаться, а не обходить её:
// Проверка означает, что сайт хочет человека. Дайте ему человека.
if (await captchaPresent(page)) {
account.status = 'NEEDS_HUMAN'
await saveAccount(account)
await page.waitForTimeout(600_000) // окно остаётся открытым для человека
return account
}
В этих логах эта ветка не сработала ни разу. Она остаётся в коде, потому что в тот день, когда сработает, правильный ход - остановиться.
Личность определяется один раз, при создании профиля
Самый частый способ потерять аккаунт - выдать ему на втором визите чуть другое устройство. Персона профиля - user agent, строки GPU, экран, точки касания, шрифты, язык - генерируется один раз при создании каталога профиля и закрепляется на диске. Каждый следующий запуск воспроизводит её же. Дрейфом не нужно управлять, потому что ничего не перегенерируется.
Поэтому же deviceType - решение момента создания:
const { page, browser } = await launch({
profile: `gh-${email}`, // каталог профиля == личность, навсегда
proxy, // собственный выходной IP этого аккаунта
label: email, // видно в окне, но не странице
deviceType: 'android', // учитывается только при первом создании
})
await page.goto('https://github.com/signup')
Передайте deviceType: 'android' - и подмена произойдёт в движке, а не в инжектированном скрипте: страница видит телефон с первого байта первого запроса, включая заголовки, которые самой странице недоступны. Отсюда же следует, что обвязка ведёт мобильную форму регистрации GitHub, где поля лежат внутри веб-компонентов; селекторы пришлось писать под неё, а не под десктопную вёрстку.
Устройство не стоит принимать на веру. Прочитайте его обратно изнутри страницы:
const d = await page.evaluate(() => ({
ua: navigator.userAgent,
platform: navigator.platform,
mobile: navigator.userAgentData?.mobile ?? null,
touch: navigator.maxTouchPoints,
screen: `${screen.width}x${screen.height}`,
dpr: devicePixelRatio,
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
}))
В логе на каждом запуске это дало 62 согласованных замера из 63:
[device:register] ✓ Android | platform=Linux armv81 mobile=true touch=5 412x917@1.75x tz=America/Los_Angeles
[device:register] UA=Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 ... Mobile Safari/537.36
Единственный замер, вернувший Win32, принадлежал десктопному профилю, созданному до начала «телефонных» прогонов. Он отчитался десктопом, потому что он и есть десктоп. Это проверка сработала, а не провалилась.
Прогрев - это в основном про то, что сессия выживает
За 32 сессии прогрева строка «сессия не сохранилась» встречается в логах ноль раз. Каждая из них открывала профиль и попадала на github.com/dashboard уже залогиненной - без пароля, без письма с подтверждением устройства, без второго фактора. Запасной путь через форму входа пока лежит мёртвым грузом, и это желаемое состояние.
Именно эту часть недооценивают. Аккаунт, которому приходится логиниться заново на каждом визите, - это аккаунт, который на каждом визите создаёт событие входа из новой сессии, то есть ровно тот сигнал, которого повторный вход и должен был избежать. Cookies, local storage, IndexedDB и passkey лежат в каталоге профиля и возвращаются вместе с ним.
Поведение поверх этого - будничное. За восемь дней было открыто 661 страница репозиториев, и ничто в том, как их читали, не является мгновенным или единообразным:
export async function typeLikeHuman(page: Page, selector: string, text: string) {
const el = page.locator(selector).first()
await el.click()
await sleep(120, 400)
for (const ch of text) {
await page.keyboard.type(ch, { delay: rand(60, 180) }) // посимвольно
}
await sleep(150, 500)
}
export async function humanScroll(page: Page, steps = randInt(4, 9)) {
for (let i = 0; i < steps; i++) {
await page.mouse.wheel(0, rand(300, 800))
await sleep(1200, 3800) // пауза на чтение
if (Math.random() < 0.2) {
await page.mouse.wheel(0, -rand(80, 250)) // взгляд назад вверх
await sleep(600, 1500)
}
}
}
page.fill() записывает строку из 40 символов одним событием, без единого keydown. Посимвольный ввод по 60-180 мс с паузами по краям - не хитрость; это просто разница между следом событий ввода, похожим на человека, и следом, не похожим.
Один аккаунт - один выходной IP, навсегда
Каждый аккаунт создаётся за резидентным прокси, и этот прокси хранится в строке аккаунта, так что все последующие сессии выходят тем же путём:
const { page } = await launch({
profile: account.browserProfile,
proxy: account.proxy, // тот же выходной IP, что и в день создания
label: account.email,
})
Часовой пояс следует за прокси автоматически: SDK перед запуском определяет гео выходного IP и записывает его в отпечаток - поэтому замер выше показывает America/Los_Angeles, тогда как хост-машина стоит в UTC+8. Локаль, часовой пояс и IP согласуются между собой, и никакой таблицы соответствий вести не нужно.
За восемь дней ни одну регистрацию не развернула заглушка GitHub Access is temporarily restricted. Эта страница привязана к выходному IP, а не к браузеру, так что это свойство пула прокси - и именно на этот вход стоит тратить деньги, если выбирать приходится.
Где на самом деле были потери
Выжили 81,7%. Остальное стоит прочитать внимательно, потому что детектированием там не пахнет.
Девять жёстких неудач прозаичны: восемь и больше подряд кандидатов в ники отвергнуты как занятые, либо поле формы не дождалось таймаута. Десять отложенных аккаунтов - это регистрации, где оверлей GitHub перехватил указатель на кнопке Create account, и страница просто осталась на /signup. Проблема селектора, чинится, к отпечатку отношения не имеет.
Пакеты к тому же не были однородны, и вот на какую картинку стоит закладываться:
Лучший прогон из десяти: 7 активных. Худший: 3, причём все пять отложенных застряли на одной и той же перехваченной кнопке. Если вы считаете бюджет - считайте по худшему числу. Разброс успешности пакета от 30% до 70% из-за фронтенд-селекторов - это норма, и это не то, что делает отпечаток.
Что покрывает бесплатный тариф
Самое интересное: почти всё описанное работает на бесплатном плане.
| Free | Платные | |
|---|---|---|
| Движок и подмена отпечатка | Тот же бинарник, та же конфигурация | Идентично |
| Локальные профили | Без ограничений | Без ограничений |
| Персона закреплена на диске и воспроизводится | Да | Да |
| Подмена под Android-устройство | Да | Да |
| Одновременных браузеров | 1 | от 5 до 100 |
Свои прокси через launch({ proxy }) |
Да | Да |
| Облачная синхронизация,управляемые прокси, Live View | Нет | Да |
Подмена отпечатка не является платной функцией. Бесплатный ключ скачивает ту же сборку движка и получает ту же сгенерированную персону; урезанного режима отпечатка не существует.
Бесплатный план ограничивает пропускную способность и удобство: один браузер за раз и ничего не сохраняется вне машины. Последовательный цикл - зарегистрировать один, прогреть один, закрыть, следующий - укладывается в этот лимит ровно, и именно так были устроены чередующиеся пакеты в этих данных. Уберите sync: true, принесите свои прокси - и весь восьмидневный прогон воспроизводится на бесплатном ключе. (Измерительный ключ здесь был с высоким лимитом одновременности, потому что два пакета шли в параллельных дорожках и потому что для ценных профилей хотелось облачной копии. Для самого цикла ни то, ни другое не требуется.)
Плата за это реальна, и её стоит назвать: на бесплатном тарифе профиль существует только на этом диске. Потеряете машину - потеряете все личности на ней, а каждая стоила регистрации, письма с подтверждением и недели истории. Ради этого и существует облачная синхронизация, и это единственная строка платной колонки, которую трудно обойти.
Как воспроизвести
Направьте это на площадку, которую вам разрешено автоматизировать, и проверьте три вещи, которые действительно важны:
- Согласованность - логируйте замер устройства на каждом запуске и сравнивайте между сессиями. Всё, что меняется между визитами, - это баг.
- Сохранность - закройте браузер, откройте тот же профиль и попадите на залогиненную страницу, не вводя пароль. Если не получается, прогрева не происходит, каким бы человечным ни был скроллинг.
- Непротиворечивость - страна выходного IP, часовой пояс и
navigator.languageдолжны рассказывать одну историю. Проверяйте детектором, а не собственным кодом.
И гоняйте это неделю, а не вечер. Всё, что в таблицах выше, стало видно примерно на третий день.
Подробнее о том, почему важно закрепление, - в статье почему аккаунты всё равно получают пометки; о компромиссе «профиль на прогон» - во временных профилях для автоматизации; о половине, отвечающей за сессию, - в passkey и многоаккаунтных сценариях.
Одну границу стоит проговорить прямо, раз уж в тексте названы реальные площадки: у каждой из них есть правила, и автоматизация регистраций не входит в них автоматически. Прочитайте их, оставайтесь внутри них, и когда сервис ставит перед вами проверку на человека - относитесь к ней как к ответу, а не как к препятствию. Всё измеренное выше работает потому, что браузер последовательно говорит о себе правду, а не потому, что он побеждает челлендж.
Попробуйте на бесплатном тарифе.
Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.