Аналіз лог-файлів: поведінка пошукових роботів під мікроскопом
Лог-файли веб-сервера — єдине джерело правди про поведінку пошукових роботів. На відміну від 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; -- Query: top non-indexed URLs 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 днів. Вартість розраховується індивідуально, залежить від об'єму логів та складності інфраструктури. Пишіть — оцінимо ваш проєкт.
Отримайте консультацію з аналізу логів вашого сайту. Ми допоможемо виявити приховані проблеми індексації та зекономити ресурси сервера. Зв'яжіться з нами для оцінки вашого проєкту — ми підберемо оптимальний стек і розрахуємо терміни.







