MAATRIX / Блог / Парсеры выедают сервер: как отделить их от поисковых роботов

Парсеры выедают сервер: как отделить их от поисковых роботов

MAATRIX

CPU на сервере упирается в потолок, база отдаёт запросы медленнее обычного, а в логах — десятки тысяч запросов от одного User-Agent, методично проходящего каждую страницу каталога, все комбинации фильтров и пагинацию до конца. Первая реакция — забанить всё, что похоже на робота, по IP или по подсети. Но в этом же трафике почти всегда сидят Googlebot и Яндекс.Бот, без которых сайт вылетит из поиска за пару недель. Ниже — как достоверно отличить настоящего поискового робота от подделки под его заголовки, распознать агрессивный парсинг по логам и настроить rate limiting так, чтобы под раздачу попали только те, кто её заслужил.

Почему нельзя банить по User-Agent

User-Agent — это просто текстовая строка, которую клиент присылает сам и которую сервер обязан принять на веру, если не проверяет ничего сверх этого. Подделать её — одна строка кода в любом скрипте:

curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://example.com/catalog

Любой парсер на Python, Scrapy, Node.js или самописном curl-скрипте может представиться Googlebot, YandexBot или Bingbot — и сервер, который фильтрует трафик только по этой строке, доверчиво пропустит его мимо любых защитных правил. Это не гипотетическая уязвимость, а стандартный приём: конкурентам, которые парсят цены и ассортимент, выгодно маскироваться именно под поискового робота, потому что администраторы часто настраивают для таких UA исключения из rate limiting «на всякий случай, чтобы не потерять индексацию».

Обратная ошибка не менее болезненна: если забанить все запросы с непонятным или ботоподобным UA по подсетям, под раздачу попадёт часть реальных пользователей за корпоративным NAT и часть легитимных сервисов мониторинга. О том, почему IP-адрес — тоже ненадёжный критерий и как из-за этого страдают честные клиенты, подробно разобрано в статье про механизм rate limiting. Здесь та же логика справедлива и для ботов: единственный способ действовать без ложных срабатываний в обе стороны — проверять не то, что клиент заявляет о себе, а то, кто на самом деле стоит за IP-адресом.

Обратный DNS-lookup: как на самом деле подтвердить бота

Google, «Яндекс» и Bing официально рекомендуют один и тот же способ верификации своих роботов — двухэтапную проверку через DNS, без всяких API-ключей и токенов.

Шаг 1. Обратный lookup. По IP-адресу, с которого пришёл запрос, получаем доменное имя:

dig +short -x 66.249.66.1
# 1.66.249.66.crawl-66-249-66-1.googlebot.com.

Шаг 2. Проверка домена. Полученное имя должно оканчиваться на официальный домен нужного робота:

РоботUser-Agent содержитPTR должен заканчиваться на
GooglebotGooglebot.googlebot.com или .google.com
YandexBotYandexBot.yandex.ru, .yandex.net или .yandex.com
Bingbotbingbot.search.msn.com

Шаг 3. Прямой lookup обратно (forward-confirm). Это обязательный шаг, без которого вся проверка бессмысленна: злоумышленник вполне может завести PTR-запись для своего сервера, указывающую на поддомен вида fake.googlebot.com.evil-domain.ru — формально строка содержит «googlebot.com», но это подделка. Поэтому имя из шага 1 резолвится обратно в IP, и результат должен совпасть с исходным адресом:

dig +short 1.66.249.66.crawl-66-249-66-1.googlebot.com
# 66.249.66.1

Совпало — перед вами настоящий Googlebot. Не совпало или PTR-записи вовсе нет — это либо чужой сервер с поддельным заголовком, либо прокси, через который парсер маскируется под поискового робота.

Вот простой скрипт, который делает обе проверки за один вызов:

#!/bin/bash
# verify-bot.sh 66.249.66.1
IP="$1"
PTR=$(dig +short -x "$IP" | sed 's/\.$//')

if [ -z "$PTR" ]; then
    echo "NOT VERIFIED: нет PTR-записи для $IP"
    exit 1
fi

case "$PTR" in
    *.googlebot.com|*.google.com) ;;
    *.search.msn.com) ;;
    *.yandex.ru|*.yandex.net|*.yandex.com) ;;
    *)
        echo "NOT VERIFIED: неизвестный домен PTR — $PTR"
        exit 1
        ;;
esac

FORWARD=$(dig +short "$PTR" | tail -n1)
if [ "$FORWARD" = "$IP" ]; then
    echo "VERIFIED: $IP -> $PTR -> $FORWARD"
else
    echo "SPOOFED: обратное разрешение $PTR дало $FORWARD, а не $IP"
    exit 1
fi

Важный практический момент: делать этот запрос синхронно, на каждый входящий HTTP-запрос — плохая идея. Каждый dig — это дополнительная сетевая задержка в десятки-сотни миллисекунд (точная цифра зависит от вашего резолвера и сети, тут нет универсального числа), и при живом трафике вы просто добавите тормозов всем посетителям. Правильная схема — асинхронная: фоновый процесс раз в несколько минут разбирает лог, проверяет новые IP, претендующие на роль известных ботов, и обновляет allow-list, который nginx читает мгновенно, без единого DNS-запроса на лету. Как собрать такой allow-list и подключить его к nginx — в разделе про rate limiting ниже.

Дополнительно Google публикует машиночитаемый список IP-диапазонов Googlebot — его можно подтягивать как второй, более быстрый источник. Но полагаться только на списки диапазонов рискованно: они периодически меняются, и не для каждого робота публикуются в удобном виде. Reverse DNS — более универсальный метод, не зависящий от конкретного провайдера.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Паттерны агрессивного парсинга: что искать в логах

Настоящий поисковый бот, даже интенсивно сканирующий сайт, обычно ведёт себя предсказуемо: соблюдает robots.txt, не долбит одну и ту же тяжёлую страницу десятками параллельных запросов, распределяет нагрузку во времени. Агрессивный парсер — конкурента, сборщика цен, датасет-скрейпера под ИИ — почти всегда выдаёт себя одним или несколькими из следующих признаков.

Высокая частота с одного источника. Быстрый способ найти подозрительных клиентов — посчитать запросы по IP за последние минуты:

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Если легитимный посетитель делает 5-10 запросов в минуту, а один адрес генерирует сотни — это либо бот, либо очень активный пользователь с автообновлением страницы; дальше смотрим остальные признаки.

Игнорирование robots.txt. Сверьте набор URL, которые запрашивает подозрительный IP, с правилами Disallow в robots.txt. Поисковые роботы крупных систем эти правила соблюдают почти всегда; самописные скрайперы конкурентов — крайне редко, потому что для них ваш robots.txt не более чем текстовый файл, который никто не парсит намеренно.

Отсутствие пауз между запросами. Легитимный краулер обычно распределяет обход во времени; агрессивный парсер шлёт запросы практически без интервалов, стремясь выгрузить максимум данных за минимум времени. Ориентировочно: если интервалы между запросами одного клиента стабильно меньше 100-200 мс на протяжении сотен запросов подряд — это не человек и, скорее всего, не вежливо настроенный краулер. Конкретный порог у вас может отличаться в зависимости от размера сайта и типа контента, это ориентир, а не жёсткая граница.

Линейный обход по параметрам. Классический почерк парсера — последовательный перебор: ?page=1, ?page=2, ?page=3… или id=1001, id=1002, id=1003… без пауз и без «человеческой» логики переходов (клика по конкретным товарам, возврата назад, использования поиска).

Удар по дорогим эндпоинтам. Парсеры особенно любят страницы, которые дорого генерировать: поиск, фильтры с множеством комбинаций параметров, экспорт в PDF/CSV, страницы сравнения. Именно эти URL стоит проверять в первую очередь — статическая страница выдержит тысячи запросов без проблем, а поиск с полнотекстовым запросом к базе на каждый хит — нет. Методика поиска, что именно у вас является узким местом при большом числе параллельных клиентов — канал, CPU или лимиты на стороне бэкенда, — разобрана в статье про параллелизм парсеров.

Один UA — много IP. Если один и тот же характерный User-Agent (не браузерный, а типа python-requests/2.31, Scrapy/2.11 или кастомная строка) приходит одновременно с десятков разных адресов — это, скорее всего, распределённый скрейпер через пул прокси, а не совпадение.

Избирательный rate limiting в nginx

Смысл избирательности прост: ограничивать частоту запросов нужно не для всего трафика одинаково, а по трём разным политикам — для подтверждённых ботов, для подозрительных «ботоподобных» клиентов и для обычных пользователей.

Сначала — geo-блок с подтверждёнными IP ботов (файл обновляется автоматически, см. следующий раздел):

# /etc/nginx/conf.d/verified-bots.conf — генерируется скриптом, руками не редактировать
geo $verified_bot {
    default 0;
    include /etc/nginx/verified_bots.list;
}

Файл verified_bots.list содержит подтверждённые адреса и диапазоны:

66.249.66.0/24 1;
5.255.253.0/24 1;

Дальше — ключ для лимитера: у подтверждённых ботов ключ пустой, а пустой ключ в limit_req_zone означает «лимит не применяется» — это штатное поведение nginx, а не костыль:

map $verified_bot $limit_key {
    1       "";
    default $binary_remote_addr;
}

limit_req_zone $limit_key zone=general:10m rate=10r/s;
limit_req_zone $limit_key zone=expensive:10m rate=2r/s;

И применяем это точечно — жёстче на дорогих страницах, мягче на остальных:

server {
    listen 443 ssl;
    server_name example.com;

    location /search {
        limit_req zone=expensive burst=5 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }

    location /catalog/filter {
        limit_req zone=expensive burst=5 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }

    location / {
        limit_req zone=general burst=20 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }
}

Такая схема защищает именно тяжёлые для сервера места, не трогая при этом статику и лёгкие страницы, и полностью выводит из-под лимита подтверждённых поисковых роботов, оставляя лимит для всех остальных — включая тех, кто представляется ботом, но не прошёл проверку. Общий механизм работы limit_req_zone, зачем нужны burst и nodelay и какие побочные эффекты бывают у наивной настройки — подробно в статье как работает rate limiting.

Автоматизация: allow-list, который обновляется сам

Ручное ведение списка подтверждённых IP работает ровно до первого изменения диапазонов у поисковика — и тогда «настоящий» Googlebot внезапно начинает получать 429 наравне с парсерами. Решение — периодический скрипт, который сам находит претендентов в логах, проверяет их через verify-bot.sh и обновляет verified_bots.list.

#!/bin/bash
# update-bot-allowlist.sh — запускать по cron, например раз в 15-30 минут
LOG=/var/log/nginx/access.log
OUT=/etc/nginx/verified_bots.list
TMP=$(mktemp)

# берём IP тех, чей UA заявляет о принадлежности к известным ботам
grep -Ei "googlebot|yandexbot|bingbot" "$LOG" \
    | awk '{print $1}' | sort -u | while read -r ip; do
        if ./verify-bot.sh "$ip" | grep -q "^VERIFIED"; then
            echo "$ip/32 1;" >> "$TMP"
        fi
    done

if ! cmp -s "$TMP" "$OUT"; then
    mv "$TMP" "$OUT"
    nginx -t && systemctl reload nginx
else
    rm -f "$TMP"
fi

Смысл схемы — не гонять dig на каждый живой запрос, а держать список актуальным фоновой сверкой. Диапазоны у крупных поисковиков меняются нечасто, но раз в 15-30 минут пересобирать список — разумный запас без заметной нагрузки на резолвер.

Для клиентов, которые представляются ботом, но верификацию не прошли и при этом продолжают агрессивно долбить сервер, имеет смысл не просто полагаться на limit_req, а подключить fail2ban, который будет банить такие IP на уровне файрвола после превышения порога — это снимает нагрузку раньше, чем запрос вообще доходит до nginx worker'а. Конкретные фильтры и jail.local под такие сценарии разобраны в статье про настройку fail2ban для nginx.

Отдельный дешёвый приём — honeypot-путь в robots.txt: добавьте правило Disallow: /internal-no-index/, но нигде на сайте не ссылайтесь на этот адрес явно. Человек и соблюдающий правила бот на него не попадут, а вот парсер, который использует robots.txt как справочник путей, а не как список запретов, рано или поздно туда зайдёт. Хит по этому пути — сигнал автоматически добавить IP в блок-лист.

Что делать, если бот всё равно продолжает долбить

Даже с настроенной верификацией и лимитами останутся клиенты, которые продолжат генерировать нагрузку — например, через пул из сотен residential-прокси, где каждый отдельный IP укладывается в лимит, а суммарно это всё равно заметная нагрузка. Здесь помогают три вещи одновременно.

Во-первых, кэширование дорогих страниц (nginx или отдельный слой типа Redis/Varnish) — если результат поиска или фильтра кэшируется на 30-60 секунд, параллельные запросы с одинаковыми параметрами бьют в кэш, а не в базу, независимо от того, кто их шлёт.

Во-вторых, limit_req_status 429 с корректным Retry-After — часть парсеров (многие фреймворки типа requests/scrapy с включённым retry) реагируют на 429 паузой, а не бесконечным повтором.

В-третьих, регулярная сверка: раз в неделю-две вручную проверяйте несколько забаненных IP через verify-bot.sh — если среди них обнаружится настоящий поисковый бот, это сигнал, что allow-list отстал от реальности или скрипт обновления сломался. Потеря доступа Googlebot к сайту на несколько дней бьёт по позициям в поиске куда болезненнее, чем те же дни повышенной нагрузки от парсера.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли просто доверять официальным спискам IP-диапазонов и не делать обратный DNS?

Можно использовать их как быстрый первый фильтр, но списки периодически меняются и покрывают не всех роботов одинаково подробно. Обратный DNS с прямым подтверждением — более универсальный и надёжный способ, особенно для роботов, чьи диапазоны не публикуются в удобном машиночитаемом виде.

Настоящий бот всё равно попадает под лимит — что не так?

Чаще всего либо allow-list устарел и не подхватил новый IP робота, либо запрос пришёл с адреса, ещё не встречавшегося в логах на момент последнего обновления списка. Проверьте IP вручную через verify-bot.sh и, если он подтверждается, добавьте его вручную, не дожидаясь следующего цикла cron.

Достаточно ли заблокировать все IP не из своей страны?

Нет. Многие парсеры используют residential-прокси именно в той стране, где находится целевая аудитория сайта, а часть легитимных сервисов и ИИ-краулеров физически размещена в дата-центрах за рубежом. Гео-блокировка бьёт мимо цели и создаёт ложное чувство защищённости.

Как быть с ИИ-краулерами вроде GPTBot или ClaudeBot?

Это не поисковые роботы, от которых зависит SEO-трафик, поэтому политику для них разумно вести отдельно от Googlebot/YandexBot — вплоть до полной блокировки через robots.txt или UA-фильтр, если вы не хотите отдавать контент для обучения моделей. Смешивать их с задачей защиты от парсеров конкурентов не стоит — это разные категории трафика с разными рисками.

Стоит ли вместо всего этого поставить Cloudflare или другой anti-bot сервис перед сервером?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →