← Все статьи

Регистрация и прогрев 109 аккаунтов на GitHub и Reddit: восемь дней логов

11 мин чтения автоматизацияаккаунтыотпечатокsdk

Зарегистрироваться - простая половина задачи. Аккаунт чего-то стоит только если через неделю им всё ещё можно пользоваться, с той же личности, без повторного входа и без цикла подтверждений. Поэтому единственное измерение, которое имеет смысл, длится днями, а не один запуск.

Вот такое измерение: обвязка на нашем SDK, которая регистрирует аккаунты на GitHub, Reddit и Hacker News, а потом раз в день-два открывает каждый из них заново и работает им так, как это делал бы человек. Восемь дней, с 2026-08-05 по 2026-08-13. Каждый запуск шёл через AntiDetectBrowser.launch(), каждое действие писалось в Postgres, а пакетные прогоны оставили на диске 4 125 строк логов. Цифры ниже взяты из этой базы и из этих логов, а не из демо.

Итоги восьми дней

0
интерактивных проверок
на 109 регистраций
0
повторных входов
за 32 сессии прогрева
0
блокировок по IP
на регистрации
62/63
замеров устройства
совпали с заданным
Аккаунтов создано обвязкой 109
Живы на конец периода 89 (81,7%)
Записанных действий 1 989
Открыто страниц репозиториев при прогреве 661
Сессий прогрева начато / завершено 146 / 141 (96,6%)

Выборка перекошена намеренно. На GitHub пришлось 103 аккаунта, потому что это самая сложная цель: форма регистрации за оверлейной антибот-проверкой, код на почту и пространство имён, где почти все короткие ники уже заняты. На Reddit - 5, на Hacker News - 1: этого хватает, чтобы убедиться, что схема работает, и совершенно недостаточно, чтобы называть процент.

активныотложены на человеканеудача
GitHub n=103 84 активны 10 отложены на человека 9 неудач 84 10 9 Reddit n=5 4 активны 1 отложен на человека 4 1
Исход регистрации по площадкам, каждая полоса масштабирована к своему итогу. Hacker News (n=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. Проблема селектора, чинится, к отпечатку отношения не имеет.

Пакеты к тому же не были однородны, и вот на какую картинку стоит закладываться:

активныотложены на человеканеудача
12 авг 10:35 7 активны 2 отложены 1 неудача 7 2 1 13 авг 01:01 3 активны 5 отложены 2 неудачи 3 5 2 13 авг 09:52 7 активны 1 отложен 1 неудача 7 1 1
Три подряд идущих пакета по GitHub: тот же код, тот же пул прокси, общая шкала. Средний прогон потерял пять аккаунтов на одном перехваченном клике.

Лучший прогон из десяти: 7 активных. Худший: 3, причём все пять отложенных застряли на одной и той же перехваченной кнопке. Если вы считаете бюджет - считайте по худшему числу. Разброс успешности пакета от 30% до 70% из-за фронтенд-селекторов - это норма, и это не то, что делает отпечаток.

Что покрывает бесплатный тариф

Самое интересное: почти всё описанное работает на бесплатном плане.

Free Платные
Движок и подмена отпечатка Тот же бинарник, та же конфигурация Идентично
Локальные профили Без ограничений Без ограничений
Персона закреплена на диске и воспроизводится Да Да
Подмена под Android-устройство Да Да
Одновременных браузеров 1 от 5 до 100
Свои прокси через launch({ proxy }) Да Да
Облачная синхронизация,управляемые прокси, Live View Нет Да

Подмена отпечатка не является платной функцией. Бесплатный ключ скачивает ту же сборку движка и получает ту же сгенерированную персону; урезанного режима отпечатка не существует.

Бесплатный план ограничивает пропускную способность и удобство: один браузер за раз и ничего не сохраняется вне машины. Последовательный цикл - зарегистрировать один, прогреть один, закрыть, следующий - укладывается в этот лимит ровно, и именно так были устроены чередующиеся пакеты в этих данных. Уберите sync: true, принесите свои прокси - и весь восьмидневный прогон воспроизводится на бесплатном ключе. (Измерительный ключ здесь был с высоким лимитом одновременности, потому что два пакета шли в параллельных дорожках и потому что для ценных профилей хотелось облачной копии. Для самого цикла ни то, ни другое не требуется.)

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

Как воспроизвести

Направьте это на площадку, которую вам разрешено автоматизировать, и проверьте три вещи, которые действительно важны:

  1. Согласованность - логируйте замер устройства на каждом запуске и сравнивайте между сессиями. Всё, что меняется между визитами, - это баг.
  2. Сохранность - закройте браузер, откройте тот же профиль и попадите на залогиненную страницу, не вводя пароль. Если не получается, прогрева не происходит, каким бы человечным ни был скроллинг.
  3. Непротиворечивость - страна выходного IP, часовой пояс и navigator.language должны рассказывать одну историю. Проверяйте детектором, а не собственным кодом.

И гоняйте это неделю, а не вечер. Всё, что в таблицах выше, стало видно примерно на третий день.

Подробнее о том, почему важно закрепление, - в статье почему аккаунты всё равно получают пометки; о компромиссе «профиль на прогон» - во временных профилях для автоматизации; о половине, отвечающей за сессию, - в passkey и многоаккаунтных сценариях.

Одну границу стоит проговорить прямо, раз уж в тексте названы реальные площадки: у каждой из них есть правила, и автоматизация регистраций не входит в них автоматически. Прочитайте их, оставайтесь внутри них, и когда сервис ставит перед вами проверку на человека - относитесь к ней как к ответу, а не как к препятствию. Всё измеренное выше работает потому, что браузер последовательно говорит о себе правду, а не потому, что он побеждает челлендж.

Попробуйте на бесплатном тарифе.

Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.