Как понять, что сайт парсят: 7 признаков в логах и Метрике
Парсинг редко выглядит как атака. Сайт открывается, заказы идут, мониторинг молчит. Просто кто-то каждую ночь забирает ваш каталог целиком — с ценами, описаниями и остатками — и делает это тихо, потому что ему выгодно, чтобы вы не замечали. Единственное место, где это видно, — журнал запросов веб-сервера. Ниже семь признаков, которые стоит проверить, и команды, которыми это делается за несколько минут.
Где искать
Понадобятся два источника. Первый — access.log nginx (у Apache то же самое, отличаются только пути): в нём есть каждый запрос, включая сделанные без браузера. Второй — Яндекс Метрика или другой счётчик: он считает только тех, у кого выполнился его JavaScript. Сравнивать стоит динамику, а не вычитать одно из другого: запросы, просмотры и визиты — разные единицы, часть людей не попадает в счётчик из-за блокировщиков, часть запросов может обслуживать CDN, а боты с JavaScript, наоборот, в Метрику попадают. Если запросов стало заметно больше, а просмотров нет, это повод искать источник, а не готовый диагноз. Примеры ниже рассчитаны на стандартный формат combined; если вы меняли log_format, поправьте номера полей.
Ещё одна оговорка: если перед сервером стоит балансировщик или CDN, в первом поле лога будет его адрес, а реальный — в заголовке X-Forwarded-For. Тогда либо включите real_ip в nginx, либо смотрите на соответствующее поле. Доверяйте этому заголовку только от адресов вашего балансировщика (set_real_ip_from): любой клиент может подставить его сам.
И главное правило на всю статью: ни один признак сам по себе парсинг не доказывает. Ищите сочетание — массовый обход карточек, повторяемость изо дня в день, большую долю охваченного каталога и характерные источники запросов.
1. Запросов много, визитов нет
Самый заметный признак. Постройте почасовую гистограмму запросов к HTML-страницам и сравните её с отчётом Метрики по часам. У людей кривая суточная: пик днём и вечером, провал ночью. Если в логе в четыре утра столько же запросов, сколько в обед, а Метрика показывает три визита, — люди так не ходят. Прежде чем делать выводы, исключите сбой счётчика и разницу часовых поясов между логом и отчётом.
awk '{print substr($4, 2, 14)}' /var/log/nginx/access.log | sort | uniq -c
Команда выводит число запросов за каждый час. Статику можно отсечь заранее, оставив только страницы: добавьте перед awk фильтр вроде grep -v '\.\(css\|js\|png\|jpg\|webp\|svg\|woff2\)'.
2. Один адрес — тысячи страниц
Человек за визит открывает десяток страниц, редко — сотню. Парсер за ночь обходит весь каталог. Посмотрите, сколько запросов приходится на каждый IP-адрес:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Команда считает все запросы адреса, включая статику: отсеките её тем же фильтром, что выше, и ограничьте интервал, например одной ночью. Помните и про NAT: за одним адресом офиса или мобильного оператора могут стоять сотни людей. В верхних строках обычно оказываются ваш же мониторинг, поисковые роботы и — если сайт парсят — несколько адресов с числами, которые человеку не набрать. Тот же расчёт полезно сделать в Метрике: если просмотров на визит вдруг стало в разы больше обычного, счётчик поймал бота с включённым JavaScript.
3. Обход по порядку
Люди ходят по сайту хаотично: главная, категория, карточка, назад, поиск. Скрипт идёт по списку. Возьмите подозрительный адрес из предыдущего пункта и посмотрите, что именно он запрашивал:
grep '^203.0.113.7 ' /var/log/nginx/access.log | awk '{print $7}' | head -50
Характерные картины: карточки товаров по возрастанию идентификатора (/product/1041, /product/1042, /product/1043), страницы пагинации подряд от первой до последней, или сначала запрос sitemap.xml, а потом все URL из него в том же порядке. Отдельно проверьте, кто вообще читал карту сайта: grep sitemap.xml access.log | awk '{print $1}' | sort | uniq -c. Поисковики там будут, но не только они.
4. Рост 404 и несуществующей пагинации
Парсер не знает, где кончается каталог, и часто написан небрежно. Он запрашивает ?page=57 у категории, где страниц двенадцать, подставляет идентификаторы удалённых товаров, перебирает варианты URL. Всё это оседает в логе как 404:
awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -30
Резкий рост доли 404 без изменений на сайте — повод посмотреть, кто их генерирует. Оговорка: всплеск 404 на путях вроде /wp-admin или /.env — это сканер уязвимостей, а не парсер; для парсинга характерны именно несуществующие карточки и страницы пагинации. Для скриптов, перебирающих пагинацию, полезно отдельно вывести запрошенные номера страниц, отсортированные по номеру — самые большие окажутся в конце:
grep -oE 'page=[0-9]+' access.log | cut -d= -f2 | sort -n | uniq -c | tail
5. Одинаковые или странные User-Agent
User-Agent — строка, которую клиент сообщает о себе сам, поэтому доверять ей нельзя, но посмотреть стоит:
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Что должно насторожить: библиотеки и инструменты, которые не скрываются (python-requests, Go-http-client, curl, Scrapy, okhttp), пустой User-Agent, устаревшая версия браузера на тысячах запросов, или одна и та же строка с десятков разных адресов. Более аккуратные парсеры копируют User-Agent настоящего Chrome, поэтому отсутствие странных строк ещё ничего не доказывает.
6. Страницы без стилей и картинок
Браузер, открыв страницу, тут же запрашивает CSS, скрипты, шрифты и изображения. Парсеру нужен только HTML. Сравните для подозрительного адреса общее число запросов и число запросов к статике:
grep '^203.0.113.7 ' access.log | wc -l
grep '^203.0.113.7 ' access.log | grep -c '\.\(css\|js\|png\|jpg\|webp\|svg\|woff2\)'
Сотни страниц и ноль статики — сильный признак автоматизации. Две оговорки. Если картинки и стили отдаёт CDN или отдельный домен, в этом логе их не будет ни у кого, и признак не работает. И у вернувшегося посетителя стили уже лежат в кэше браузера, поэтому смотрите на первые запросы адреса, а не на середину сессии.
7. Адреса дата-центров, подсети и ночные часы
Домашние и мобильные пользователи приходят с адресов операторов связи. Парсеры чаще всего работают с арендованных серверов, и их адреса принадлежат хостингам и облакам. Владельца конкретного адреса покажет whois; для массовой проверки удобнее сгруппировать запросы по подсетям:
awk '{split($1, a, "."); print a[1]"."a[2]"."a[3]".0/24"}' access.log | sort | uniq -c | sort -rn | head
Команда рассчитана на IPv4; адреса IPv6 она сгруппирует бессмысленно, их проще смотреть отдельно. Если одна подсеть даёт заметную долю всех запросов, а whois говорит, что это хостинг, — это весомый аргумент, но не приговор: с адресов хостингов приходят и поисковые роботы, и мониторинг, и корпоративные VPN, и сканеры уязвимостей. Смотрите, что именно запрашивает подсеть: карточки товаров по порядку или случайные пути. Дополнительный штрих — время: скрипты часто запускают по расписанию ночью, когда сервер свободнее и меньше шанс попасться на глаза.
Что с этим делают
Два типичных сценария. Первый — мониторинг цен: конкурент или сервис аналитики каждый день снимает ваши цены и остатки, чтобы держать свои чуть ниже. Второй — копирование каталога: описания, характеристики и фотографии переезжают на чужой сайт или маркетплейс, иногда быстрее, чем их успевает проиндексировать поиск у вас. Реже — агрегаторы и сбор данных для обучения моделей. В любом случае ваш сервер оплачивает чужую работу.
Что не помогает
robots.txt. Это просьба, а не запрет. Поисковики её соблюдают, парсер — только если захочет, а он не захочет.- Запрет выделения и копирования в CSS, блокировка правой кнопки. Парсер не рендерит страницу и не видит ваших стилей: он читает HTML как текст.
- Цены картинками, текст через JavaScript. Усложняет жизнь ненадолго и мешает поисковикам больше, чем ботам.
- Блок по одному IP или одному User-Agent. Помогает против самых простых скриптов и ровно до следующего запуска с другого адреса. Подробнее — в статье о нагрузке от ботов.
Что помогает
Сначала разведите две задачи. Если проблема в нагрузке, начинайте с кэша для анонимных посетителей и лимитов на дорогие запросы: они удешевляют обслуживание ботов, но забирать каталог не мешают. Если проблема в самом сборе данных, нужны меры, которые проверяют не строку в заголовке, а клиента: проверка браузера перед сервером — посетитель без cookie получает лёгкую страницу, браузер решает на ней задачу в JavaScript, а скрипт без JS-движка до контента не доходит. Это проверка клиента, а не доказательство, что за ним человек: парсер на настоящем браузере её пройдёт, и с ним придётся работать другими сигналами. Один из вариантов такой проверки — CrawlGuard: без CAPTCHA; роботы Яндекса, Google, Bing, Mail.ru, DuckDuckGo и Apple пропускаются; подключение через DNS; тариф Free — один сайт и миллион запросов в месяц без ограничения срока. Но начните с логов: сначала стоит убедиться, что проблема есть, и понять, какого она размера.