Настройка автоматического мониторинга позиций сайта в поиске

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка автоматического мониторинга позиций сайта в поиске
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Настройка автоматического мониторинга позиций сайта в поиске

Позиции в поиске не статичны: апдейты алгоритмов Google/Яндекс, активность конкурентов, изменения на самом сайте — всё это влияет на ранжирование. Знать об изменениях через неделю-две — значит реагировать поздно. Автоматический мониторинг позиций даёт сигнал в течение 24 часов.

Источники данных

Существует три принципиально разных подхода к получению позиций:

Google Search Console API — бесплатно, данные за 28 дней, реальные позиции по всем запросам, но с задержкой 2–3 дня. Не позволяет проверить конкретную позицию «прямо сейчас». Хорошо для отслеживания общих трендов и исторического анализа.

Платные API (SE Ranking, Serpstat, DataForSEO, Serpwow) — проверяют позицию по конкретному ключевому слову для заданного региона прямо сейчас. Стоимость: от $0.001 до $0.05 за запрос в зависимости от сервиса и объёма.

Прямой парсинг SERP — технически возможен, но нарушает ToS Google/Яндекс, требует прокси-ротацию, ненадёжен. Не используется в продакшн-мониторинге.

Интеграция DataForSEO SERP API

DataForSEO — один из наиболее доступных по цене API для проверки позиций с поддержкой Google, Яндекс, Bing:

import requests
import json
from base64 import b64encode

class DataForSEOClient:
    BASE_URL = 'https://api.dataforseo.com/v3'

    def __init__(self, login: str, password: str):
        creds = b64encode(f'{login}:{password}'.encode()).decode()
        self.headers = {
            'Authorization': f'Basic {creds}',
            'Content-Type': 'application/json',
        }

    def check_positions(
        self,
        keyword: str,
        target_domain: str,
        location_code: int = 2840,  # USA; 2643 = UK, 2112 = Russia
        language_code: str = 'en',
        depth: int = 100,
    ) -> dict:
        payload = [{
            'keyword': keyword,
            'target': target_domain,
            'location_code': location_code,
            'language_code': language_code,
            'depth': depth,
        }]

        resp = requests.post(
            f'{self.BASE_URL}/serp/google/organic/live/advanced',
            headers=self.headers,
            data=json.dumps(payload),
            timeout=60,
        )
        resp.raise_for_status()
        return resp.json()

    def parse_position(self, response: dict, target_domain: str) -> dict | None:
        tasks = response.get('tasks', [])
        if not tasks:
            return None

        items = tasks[0].get('result', [{}])[0].get('items', [])
        for item in items:
            if item.get('type') == 'organic':
                domain = item.get('domain', '')
                if target_domain in domain:
                    return {
                        'position': item.get('rank_absolute'),
                        'url': item.get('url'),
                        'title': item.get('title'),
                        'featured_snippet': item.get('rank_absolute') == 0,
                    }
        return None  # не найден в топ-100

Яндекс через XML API

Для сайтов с российской аудиторией важен Яндекс. Яндекс предоставляет XML API для поиска — отдельная квота, требует регистрации:

import xml.etree.ElementTree as ET

def check_yandex_position(
    query: str,
    target_domain: str,
    user: str,
    key: str,
    region: int = 213,  # Москва
    depth: int = 100,
) -> int | None:
    url = 'https://yandex.ru/search/xml'
    params = {
        'user': user,
        'key': key,
        'query': query,
        'lr': region,
        'l10n': 'ru',
        'sortby': 'rlv',
        'filter': 'none',
        'groupby': f'attr=d.mode=deep.groups-on-page={depth}.docs-in-group=1',
    }

    resp = requests.get(url, params=params, timeout=30)
    root = ET.fromstring(resp.content)

    for i, doc in enumerate(root.findall('.//doc'), start=1):
        domain_el = doc.find('domain')
        if domain_el is not None and target_domain in domain_el.text:
            return i

    return None

Структура базы данных для хранения позиций

CREATE TABLE tracked_keywords (
    id SERIAL PRIMARY KEY,
    keyword TEXT NOT NULL,
    target_domain TEXT NOT NULL,
    search_engine VARCHAR(20) DEFAULT 'google',  -- google, yandex, bing
    location_code INTEGER,
    language_code VARCHAR(10),
    active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE position_history (
    id SERIAL PRIMARY KEY,
    keyword_id INTEGER REFERENCES tracked_keywords(id),
    position INTEGER,        -- NULL если не найден в топ-100
    url TEXT,
    checked_at DATE NOT NULL,
    UNIQUE(keyword_id, checked_at)
);

CREATE INDEX idx_positions_keyword_date
    ON position_history(keyword_id, checked_at DESC);

Ежедневный запуск мониторинга

import psycopg2
from datetime import date

def run_daily_check(db_conn, dfs_client: DataForSEOClient, target_domain: str):
    today = date.today().isoformat()

    with db_conn.cursor() as cur:
        cur.execute('''
            SELECT id, keyword, search_engine, location_code, language_code
            FROM tracked_keywords
            WHERE active = true
        ''')
        keywords = cur.fetchall()

    for kw_id, keyword, engine, loc_code, lang_code in keywords:
        try:
            response = dfs_client.check_positions(
                keyword=keyword,
                target_domain=target_domain,
                location_code=loc_code or 2840,
                language_code=lang_code or 'en',
            )
            result = dfs_client.parse_position(response, target_domain)
            position = result['position'] if result else None
            url = result['url'] if result else None

            with db_conn.cursor() as cur:
                cur.execute('''
                    INSERT INTO position_history (keyword_id, position, url, checked_at)
                    VALUES (%s, %s, %s, %s)
                    ON CONFLICT (keyword_id, checked_at) DO UPDATE
                    SET position = EXCLUDED.position, url = EXCLUDED.url
                ''', (kw_id, position, url, today))
            db_conn.commit()

        except Exception as e:
            print(f'Error checking {keyword}: {e}')

Алерты на изменения позиций

def detect_significant_changes(db_conn, threshold: int = 5) -> list[dict]:
    with db_conn.cursor() as cur:
        cur.execute('''
            WITH ranked AS (
                SELECT
                    k.keyword,
                    p.position,
                    p.checked_at,
                    LAG(p.position) OVER (
                        PARTITION BY p.keyword_id ORDER BY p.checked_at
                    ) AS prev_position
                FROM position_history p
                JOIN tracked_keywords k ON k.id = p.keyword_id
                WHERE p.checked_at >= CURRENT_DATE - INTERVAL '2 days'
            )
            SELECT keyword, prev_position, position,
                   (COALESCE(prev_position, 101) - COALESCE(position, 101)) AS change
            FROM ranked
            WHERE prev_position IS NOT NULL
              AND ABS(COALESCE(prev_position, 101) - COALESCE(position, 101)) >= %s
            ORDER BY ABS(change) DESC
        ''', (threshold,))
        return [
            {
                'keyword': row[0],
                'prev': row[1],
                'current': row[2],
                'change': row[3],
                'direction': 'up' if row[3] > 0 else 'down',
            }
            for row in cur.fetchall()
        ]

Нотификации в Telegram

import httpx

def send_telegram_alert(bot_token: str, chat_id: str, changes: list[dict]):
    if not changes:
        return

    lines = ['*Изменения позиций за сутки:*\n']
    for ch in changes[:20]:
        arrow = '↑' if ch['direction'] == 'up' else '↓'
        prev = ch['prev'] or '100+'
        curr = ch['current'] or '100+'
        lines.append(f"{arrow} `{ch['keyword']}`: {prev} → {curr}")

    text = '\n'.join(lines)
    httpx.post(
        f'https://api.telegram.org/bot{bot_token}/sendMessage',
        json={'chat_id': chat_id, 'text': text, 'parse_mode': 'Markdown'},
    )

Расчёт объёма запросов и стоимости

Для 100 ключевых слов с ежедневной проверкой — 100 API-запросов в день, 3000 в месяц. По тарифам DataForSEO ($0.003–0.005 за Google SERP) это $9–15/месяц. При 500 словах — $45–75/месяц. Яндекс XML API бесплатный при небольшом объёме (квота зависит от параметров аккаунта).

Сроки

Настройка мониторинга с хранением в PostgreSQL и Telegram-алертами для одного домена — 2–3 рабочих дня. Добавление визуализации (Grafana/Metabase), поддержки нескольких сайтов, автоматического импорта ключевых слов из GSC — 4–6 дней.