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







