Лог-файлы веб-сервера — единственный источник правды о поведении поисковых роботов. В отличие от 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 дней. Стоимость рассчитывается индивидуально, зависит от объёма логов и сложности инфраструктуры. Пишите — оценим ваш проект.
Получите консультацию по анализу логов вашего сайта. Мы поможем выявить скрытые проблемы индексации и сэкономить ресурсы сервера. Свяжитесь с нами для оценки вашего проекта — мы подберём оптимальный стек и рассчитаем сроки.







