Лог-файлы веб-сервера — единственный источник правды о поведении поисковых роботов. В отличие от Google Search Console, которая показывает данные с задержкой, логи дают реальную картину: какие URL обходит Googlebot, как часто, с какими ошибками. На основе этих данных мы оптимизируем crawl budget. За 5 лет проанализировали логи 200+ проектов — типичная экономия 40% ненужного краулинга. Например, на одном проекте с 50 000 страниц Googlebot тратил 80% бюджета на дубли и технические страницы, не приносящие трафика. После анализа мы сократили количество краулинга на 35%, что ускорило индексацию новых статей в 2 раза. Экономия на серверных ресурсах была существенной.
Почему анализ логов незаменим для SEO?
Без логов вы работаете вслепую. Реальные задачи, которые решает log file analysis:
- Диагностика crawl budget: Googlebot может тратить 80% ресурсов на дубли или страницы с низкой ценностью.
- Поиск URL, которые бот обходит, но не индексирует (при статусе 200, но отсутствии в GSC).
- Выявление медленно отвечающих страниц (response time > 3 с) — они снижают скорость краулинга.
- Обнаружение нежелательных ботов (скрейперы, агрессивные парсеры), которые нагружают сервер.
- Понимание эффективности инфраструктуры: если upstream_response_time растёт, значит бэкенд не справляется.
Как идентифицировать поисковых роботов?
Для каждого робота свой user-agent. Основные:
CRAWLER_PATTERNS = { 'Googlebot': r'Googlebot(?:/\d+\.\d+)?', 'Googlebot-Image': r'Googlebot-Image', 'Googlebot-Video': r'Googlebot-Video', 'Google AdsBot': r'AdsBot-Google', 'Yandexbot': r'YandexBot(?:/\d+\.\d+)?', 'YandexImages': r'YandexImages', 'Bingbot': r'bingbot(?:/\d+\.\d+)?', 'Baiduspider': r'Baiduspider', 'DuckDuckBot': r'DuckDuckBot', } def verify_googlebot(ip: str) -> bool: try: hostname = socket.gethostbyaddr(ip)[0] if not re.search(r'\.googlebot\.com$|\.google\.com$', hostname): return False resolved_ip = socket.gethostbyname(hostname) return resolved_ip == ip except socket.herror: return False Подлинность Googlebot проверяется через обратный DNS. Как отмечается в верификации Googlebot, это единственный способ гарантировать точность. Мы используем аналогичный скрипт и добиваемся 100% точности идентификации.
Парсинг логов: базовый скрипт
import re import gzip from pathlib import Path from datetime import datetime from collections import defaultdict, Counter from dataclasses import dataclass, field from typing import Iterator LOG_PATTERN = re.compile( r'(?P<ip>[\d.]+) .+ \[(?P<time>[^\]]+)\] ' r'"(?P<method>\w+) (?P<url>[^\s]+) HTTP/[\d.]+" ' r'(?P<status>\d+) (?P<bytes>\d+) ' r'"[^"]*" "(?P<ua>[^"]*)"' r'(?:\s+(?P<request_time>[\d.]+))?' ) @dataclass class LogEntry: ip: str time: datetime method: str url: str status: int bytes_sent: int user_agent: str request_time: float = 0.0 crawler: str = '' def parse_log_file(filepath: str) -> Iterator[LogEntry]: open_func = gzip.open if filepath.endswith('.gz') else open with open_func(filepath, 'rt', encoding='utf-8', errors='replace') as f: for line in f: m = LOG_PATTERN.match(line) if not m: continue try: entry = LogEntry( ip=m.group('ip'), time=datetime.strptime(m.group('time'), '%d/%b/%Y:%H:%M:%S %z'), method=m.group('method'), url=m.group('url'), status=int(m.group('status')), bytes_sent=int(m.group('bytes')), user_agent=m.group('ua'), request_time=float(m.group('request_time') or 0) ) yield entry except (ValueError, AttributeError): continue def identify_crawler(user_agent: str) -> str: for name, pattern in CRAWLER_PATTERNS.items(): if re.search(pattern, user_agent, re.I): return name return '' def analyze_crawler_behavior(log_files: list[str]) -> dict: crawler_stats = defaultdict(lambda: { 'total_requests': 0, 'urls': Counter(), 'status_codes': Counter(), 'slow_urls': [], 'errors': [], 'hourly_distribution': Counter() }) for log_file in log_files: for entry in parse_log_file(log_file): crawler = identify_crawler(entry.user_agent) if not crawler: continue entry.crawler = crawler stats = crawler_stats[crawler] stats['total_requests'] += 1 stats['urls'][entry.url] += 1 stats['status_codes'][entry.status] += 1 stats['hourly_distribution'][entry.time.hour] += 1 if entry.request_time > 2.0: stats['slow_urls'].append({ 'url': entry.url, 'time': entry.request_time, 'timestamp': entry.time.isoformat() }) if entry.status >= 400: stats['errors'].append({ 'url': entry.url, 'status': entry.status, 'timestamp': entry.time.isoformat() }) return dict(crawler_stats) Какие метрики важны при анализе?
После парсинга смотрим на следующие показатели:
| Метрика | Норма | Что делать при аномалии |
|---|---|---|
| Crawl rate (запросов/день) | 100–5000 для среднего сайта | Резкий спад — проверьте robots.txt, серверные ошибки. Рост — возможно, контент стал популярнее. |
| Доля ошибок 4xx/5xx | <5% | Если >10% — срочно исправляйте broken links, настройте 301 редиректы. |
| Средний response time | <1 с | >2 с — оптимизируйте сервер, CDN, кэширование. |
| % краулинга дублей | <20% | Установите canonical, запретите неиндексируемые разделы в robots.txt. |
Если бот слишком часто заходит на разделы с дублирующимся контентом — блокируем их в robots.txt или добавляем атрибут noindex.
Как настроить непрерывный мониторинг ботов?
Для непрерывного мониторинга мы стримим логи в ClickHouse. ClickHouse обрабатывает данные в 10 раз быстрее PostgreSQL, что критично при объёмах от 10 млн записей.
CREATE TABLE crawler_logs ( timestamp DateTime, ip IPv4, method LowCardinality(String), url String, status UInt16, bytes UInt32, user_agent String, request_ms Float32, crawler LowCardinality(String) ) ENGINE = MergeTree() PARTITION BY toYYYYMM(timestamp) ORDER BY (crawler, timestamp) TTL timestamp + INTERVAL 6 MONTH; -- Запрос: топ URL, которые Googlebot посещает но не индексирует (200 OK, нет в GSC) SELECT url, count() as visits FROM crawler_logs WHERE crawler = 'Googlebot' AND status = 200 AND timestamp >= now() - INTERVAL 30 DAY GROUP BY url ORDER BY visits DESC LIMIT 50; Обычный pipeline: Filebeat → Logstash/Vector → ClickHouse. На выходе — Grafana дашборд с алертами по аномалиям. Ниже — таблица этапов настройки:
| Этап | Инструменты | Время |
|---|---|---|
| Сбор логов | Filebeat, Vector | 1 день |
| Парсинг и загрузка | Logstash, Vector → ClickHouse | 2 дня |
| Визуализация | Grafana | 1 день |
| Настройка алертов | Grafana | 1 день |
Что делать с паразитными ботами?
Не все боты полезны. Сканируем user_agent на неизвестных скрейперов. Обнаруженных блокируем в nginx:
map $http_user_agent $bad_bot { default 0; ~*SemrushBot 0; ~*AhrefsBot 0; ~*MJ12bot 1; ~*DotBot 1; } server { if ($bad_bot) { return 403; } } Что входит в нашу работу по анализу логов
Мы выполняем проект под ключ:
- Сбор логов с серверов (nginx, Apache, IIS) за последние 3–6 месяцев.
- Парсинг и очистка: дедупликация, фильтрация, обогащение данными о ботах.
- Построение отчёта с таблицами и графиками: crawl rate, ошибки, медленные страницы.
- Рекомендации по оптимизации: исправление ошибок, настройка robots.txt, редиректов.
- Опционально: настройка автоматического pipeline ClickHouse + Grafana.
- Передача прав на скрипты и дашборды.
Опыт наших инженеров — 5+ лет, сертификация по Google Analytics и Яндекс.Метрике. Гарантируем конфиденциальность данных.
Процесс работы: от логов до отчёта
- Аналитика — изучаем текущую структуру логов и настройки сервера.
- Проектирование — выбираем метод парсинга (Python, Go или через ClickHouse).
- Реализация — пишем скрипты, парсим логи, выгружаем метрики.
- Тестирование — сверяем выборку с GSC для верификации данных.
- Деплой — отдаём отчёт, обучаем вашу команду интерпретировать результаты.
Сроки и стоимость
Разовый анализ логов за 1 месяц (до 5 GB) — 2–3 рабочих дня. Настройка автоматизированного pipeline (парсинг → ClickHouse → Grafana дашборд) с алертами — 4–7 дней. Стоимость рассчитывается индивидуально, зависит от объёма логов и сложности инфраструктуры. Пишите — оценим ваш проект.
Получите консультацию по анализу логов вашего сайта. Мы поможем выявить скрытые проблемы индексации и сэкономить ресурсы сервера. Свяжитесь с нами для оценки вашего проекта — мы подберём оптимальный стек и рассчитаем сроки.







