Этот разговор был у каждого инженера, который пишет краулеры. Сайт, работавший ещё вчера, теперь возвращает 403. Тот же код, тот же прокси, те же заголовки, и где-то, тихо, какая-то система решила, что ваш трафик не человеческий. Первое побуждение: найти ту единственную вещь, которая сломалась. На деле же ничего не ломалось. Детектирование стало лучше.

Современные системы защиты от ботов не привратники. Это вероятностные движки. Каждый запрос оценивается по десяткам независимых сигнальных поверхностей, и единая сводная оценка решает, пропустят ли вас, мягко придушат, предъявят ли вам челлендж или вернут отравленный ответ. Понимание этой оценки (что в неё входит, как смещаются веса и где на самом деле находятся лёгкие выигрыши для обхода) и есть разница между краулером, который проживёт квартал, и тем, что проживёт десятилетие.

Этот материал, системный взгляд. Не список заголовков, которые надо подделать. Не «пять прокси на пробу». Это карта местности в том виде, в каком мы объясняем её внутри Crawlbase, когда новый инженер приходит в платформенную команду.

Три поверхности детектирования

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

  • Снятие отпечатков на транспортном уровне: что ваше TLS-рукопожатие сообщает о стеке под ним
  • Инспекция на уровне протокола: что ваше HTTP-поведение говорит о клиенте
  • Поведенческое моделирование: как выглядит ваша сессия со временем

Они оцениваются независимо и комбинируются статистически. Пройдёмся по каждому.

Снятие отпечатков TLS

Первый сигнал приходит ещё до того, как ваш HTTP-запрос вообще будет разобран. Когда ваш клиент открывает TLS-соединение, он отправляет пакет ClientHello, в котором перечислены поддерживаемые наборы шифров, анонсируемые расширения, предпочитаемые эллиптические кривые и порядок всего этого. Этот порядок задаётся реализацией: OpenSSL даёт один отпечаток, BoringSSL другой, crypto/tls в Go третий, стек Chrome четвёртый. JA4 хеширует соответствующие компоненты в одну строку.

FIG. 01 Структура хеша JA4. Каждый сегмент кодирует свою грань рукопожатия.

Следствие: если вы делаете запрос из библиотеки requests в Python через резидентный прокси, прокси предоставляет прекрасный резидентный IP, но TLS-отпечаток объявляет «OpenSSL через Python 3.11» всякому, кто слушает. Системе детектирования не нужно смотреть на ваш IP, ваши заголовки или ваше поведение. Одно лишь рукопожатие говорит ей, что вы такое.

Примечание

Популярный обходной приём, патчинг requests на использование Chrome-подобного списка наборов шифров, проваливается ровно по той же причине, по которой и срабатывает: теперь так делает каждый краулер. Поставщики anti-bot ведут реестры отпечатков «Chrome, но на самом деле Python». Само несоответствие между заявленным user-agent и реальным рукопожатием уже является сигналом.

Инспекция HTTP-профиля

За пределами TLS-уровня допрашивается уже сам HTTP-запрос. Не содержимое: форма. Реальные браузеры отправляют заголовки в определённом порядке. Chrome отправляет :method перед :authority; Firefox меняет местами две записи с низким приоритетом. HTTP/2 вводит порядок фреймов и приоритизацию потоков, которые различаются от клиента к клиенту.

Вот как HTTP/2-отпечаток на самом деле выглядит для детектора:

JSON
{
 "akamai_hash": "1:65536;3:1000;4:6291456;6:262144|15663105|0|m,s,a,p",
 "h2_settings": {
 "HEADER_TABLE_SIZE": 65536,
 "INITIAL_WINDOW_SIZE": 6291456,
 "MAX_HEADER_LIST_SIZE": 262144
 },
 "header_order": [":method", ":authority", ":scheme", ":path"],
 "pseudo_headers": "m,a,s,p", // Chrome canonical
 "frame_priority": [256, 255, 254, 253, 252]
}

Этого блока достаточно, чтобы с высокой уверенностью идентифицировать «Chrome 122 на macOS», независимо от строки user-agent. В частности, хеш Akamai является де-факто стандартом для снятия отпечатков HTTP/2 и проверяется практически каждым крупным CDN.

Ловушка здесь тоньше, чем на TLS-уровне. Вы можете ротировать IP хоть весь день; вы можете менять user-agent на каждом запросе. Но если ваш HTTP/2-клиент всегда согласует один и тот же INITIAL_WINDOW_SIZE независимо от того, каким браузером вы притворяетесь, вы провалите проверку согласованности задолго до того, как провалите проверку отпечатка.

Цель не в том, чтобы избегать блокировок. Цель в том, чтобы сделать вашу систему антихрупкой к ним. Из нашего внутреннего инженерного руководства

Поведенческие сигналы

Третья поверхность из тех, что растут со временем, и она, безусловно, тяжелее всего убедительно подделать в масштабе. Системы детектирования строят модель того, как выглядит сессия. Реальные пользователи перемещаются. Они нажимают «назад». Они открывают ссылку в новой вкладке, оставляют её на сорок секунд, затем закрывают. Они запрашивают favicon.ico на первом обращении и больше не запрашивают. Они изредка не загружают таблицу стилей и запрашивают её повторно. Тайминг между их запросами имеет дрожание, которое следует узнаваемому распределению: логнормальному в большинстве измерений.

Краулеры, особенно промышленного класса, заточенные под пропускную способность, не делают почти ничего из этого. Мы запрашиваем статью. Мы извлекаем данные. Мы движемся дальше. Мы не блуждаем. Мы не простаиваем. Мы не кликаем по рекламе, до которой нам нет дела.

Совет для продакшена

Если интервалы запросов вашего краулера имеют коэффициент вариации ниже 0.3, вы заметно автоматизированы даже с идеальными отпечатками. Реальные пользователи держатся примерно на 0.8–1.4. Лекарство не в том, чтобы добавить случайные паузы; оно в том, чтобы моделировать времена прибытия как стохастический процесс и сэмплировать из него.

Модель совместной вероятности

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

Современный конвейер детектирования выдаёт нечто такое:

Python
def verdict(request, session) -> Verdict:
 tls_score = score_tls(request.handshake) # 0.0 – 1.0
 http_score = score_http(request.profile) # 0.0 – 1.0
 behavior_score = score_session(session) # 0.0 – 1.0

 # Weights are tuned per-customer, per-route, per-hour.
 composite = (
 0.30 * tls_score
 + 0.25 * http_score
 + 0.45 * behavior_score
 )

 if composite > 0.85: return Verdict.BLOCK
 if composite > 0.60: return Verdict.CHALLENGE
 if composite > 0.40: return Verdict.THROTTLE
 return Verdict.ALLOW

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

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

Во-вторых, пороги смещаются. Та же сводная оценка, что пропускала трафик в 2 часа ночи UTC, может предъявить ему челлендж в 11 утра. Та же оценка, что пропускает на странице публичного каталога, может заблокировать на API оформления заказа. Отношение к системе как к статической и есть источник большинства сбоев «вчера же работало».

В-третьих, и это стратегическое прозрение, блокировки не худший исход. Челлендж даёт вам обратную связь. Блокировка даёт вам обратную связь. Уровень придушивания и есть то, где целостность данных тихо умирает. Вы получаете ответы; они выглядят корректными; но ценовые данные слегка искажаются, счётчики остатков устарели, и через три недели вы обнаруживаете, что 12% вашего датасета отравлено. Проектировать под блокировки легко. Проектировать так, чтобы обнаружить, что вас тихо понизили, и есть трудная часть.

Проектирование под антихрупкий сценарий

Большинство инженерных усилий по обходу пытаются стать невидимыми. Мы пришли к убеждению, что это неправильная цель. Невидимость, это движущаяся мишень, которую поддерживает противник с большими ресурсами, чем у вас. Цель в устойчивости: построить систему, чья производительность деградирует плавно при блокировке, восстанавливается автоматически при изменении условий и выдаёт честный сигнал о собственном качестве данных.

Конкретно это означает несколько вещей в нашей инфраструктуре:

  1. Многоуровневое разнообразие отпечатков. Не одна имитация Chrome, а их популяция, сэмплируемая подобающим образом. Пул прокси, это не список IP; это совместное распределение по (IP, TLS-стек, HTTP-профиль, геолокация).
  2. Скоринг собственного трафика в реальном времени. Мы рассматриваем каждый исходящий запрос как выборку из неизвестного распределения и измеряем распределение ответов. Если p(200) на данном маршруте падает ниже базового уровня, этот маршрут автоматически помещается в карантин для диагностического обхода, прежде чем продакшен-трафик возобновится.
  3. Состязательная валидация. Мы периодически скрейпим заведомо корректный контент и проверяем, что полученное совпадает с ожидаемым. Расхождение между ожидаемым и наблюдаемым гораздо лучший сигнал состояния, чем HTTP-статусы.
Почему это работает

Поставщики anti-bot оптимизируются под отлов медианного скрейпера. Медианный скрейпер шумный: один и тот же отпечаток на миллионах запросов, никакого поведенческого моделирования, никакой валидации. Краулеру, который статистически неотличим от человеческого трафика по тем измерениям, которые меряет детектор, не нужно быть невидимым. Ему просто нужно быть ничем не примечательным.

Как выглядят цифры

Чтобы сделать это конкретным: на нашей собственной инфраструктуре за последний квартал, на выборке из топ-500 наиболее краулимых доменов, мы наблюдали примерно следующее.

  • Клиенты с единственным отпечатком (стандартные библиотеки Python/Node) достигали частоты блокировок 47% в течение первых 100 запросов против маршрута под защитой Cloudflare.
  • Клиенты с подобранным отпечатком, но без поведенческого моделирования достигали 22%.
  • Клиенты с подобранным отпечатком, поведенческим моделированием и адаптивным таймингом по маршруту достигали 3.1%.
  • Та же популяция, пропущенная через слой умной маршрутизации Crawlbase, достигала 0.4%.

Улучшение со второго уровня на третий и есть то, что имеет значение. Поведенческая модель не маргинальна. Это разница между краулером, требующим постоянной няньки, и тем, что месяцами работает без присмотра.

Встроено в Crawlbase

Цифра 0.4% выше, это наш слой умной маршрутизации, делающий то самое разнообразие отпечатков и поведенческое моделирование, описанные в этом разделе, без того, чтобы вы поддерживали хоть что-то из этого.

Итоги

Ключевые выводы

  • Детектирование ботов, это совместная вероятность по трём независимым поверхностям. Вам не нужно выигрывать на каждой. Вам нужно нигде не проиграть с разгромом.
  • Поведенческий сигнал имеет наибольший вес в современных системах. Часы работы, потраченные на доведение TLS-отпечатков до совершенства, окупаются меньше, чем день, потраченный на моделирование реалистичного поведения сессии.
  • Придушивание опаснее блокировки. Блокировка сообщает вам, что что-то не так. Придушивание тихо отравляет ваш датасет.
  • Проектируйте под устойчивость, а не под невидимость. Невидимость, это гонка вооружений, которую вы в итоге проиграете. Устойчивость накапливается.
  • Измеряйте качество собственных данных, а не только свои статус-коды. 200 OK необходимо, но недостаточно. Форма ответа и есть настоящий сигнал.

Обход anti-bot, в конечном счёте, не столько про то, чтобы победить систему, сколько про понимание того, в какую игру система на самом деле играет. Системы умнее, чем были пять лет назад. Через ещё пять они станут умнее. Преуспевают те команды, которые перестают относиться к детектированию как к стене, на которую нужно влезть, и начинают относиться к нему как к ограничению, под которое нужно проектировать, ровно так же, как вы проектировали бы под сетевую задержку, или под нагрузку на базу данных, или под любое другое свойство физической вселенной, в которой вы работаете.

Веб не хаос. У него есть структура. Мы её картируем. Вы строите на ней.

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга