SEO-кейс: как мы сделали 1 миллион продуктовых страниц

Volagratis — один из крупнейших тревел-агрегаторов в Европе. У них огромная база предложений: билеты, отели, туры. Но проблема была в другом — трафик стагнировал, поисковики индексировали сайт плохо, а конкурентные страницы забирали трафик.

Мы решили проблему радикально: создали миллион продуктовых страниц, которые не просто заполнили выдачу, а начали приносить органический трафик. Рассказываю, как спроектировали структуру, оптимизировали индексацию и не получили штрафов за дубли.
Цели проекта:

  • Протестировать массовую генерацию контента. Проверить, как автоматизированное создание продуктовых страниц влияет на трафик и индексацию.
  • Закрепить лидерство в органике. Усилить позиции по ключевым запросам, чтобы конкуренты остались за бортом.
  • Оптимизировать конверсию. Сделать страницы не просто видимыми, а продающими.
  • Снизить зависимость от платного трафика. Перенести фокус с контекста на долгосрочный SEO-эффект.

Сбор ядра: как мы нашли максимум трафика

Проанализировали всю выдачу тревел-рынка UK/EU. Посмотрели не только частотные запросы, но и редкие, локальные, сезонные. Разобрали, как пользователи ищут билеты, отели и туры.

Вскрыли конкурентов: Kayak, Skyscanner, Booking, Google Flights. Изучили, какие сценарии поиска у них работают, и добавили свои гипотезы. Собрали полную карту намерений.

Учли не только запросы вроде «Как дешевле?», но и те, которые никто не закрывает — «Когда лучше ехать?», «Что посмотреть?», «Как сэкономить?». Теперь сайт перехватывает трафик на всех этапах принятия решения.

Пример группировки ↓

Как мы создали 1 000 000 страниц в Excel через ChatGPT и выгрузили их в CMS

Создали таблицу с 20 колонками для гибкой генерации контента:
URL – ссылка на страницу.
H1 – заголовок первого уровня (динамически генерируется).
Title – оптимизированный SEO-заголовок (до 60 символов).
Meta Description – мета-описание (до 160 символов).
Body – основной текст страницы (генерируется через GPT API).
Departure City – город отправления.
Arrival City – город прибытия.
Country – страна назначения.
Region – регион/область в стране.
Best Travel Time – оптимальный период для поездки (на основе исторических данных).
Price Range – диапазон цен на билеты (парсинг по API авиакомпаний).
Airlines – список авиакомпаний, выполняющих рейсы.
Hotel Offers – цены на отели в пункте назначения (интеграция с Booking API).
Attractions – топовые достопримечательности (LSI-фразы).
FAQ – частые вопросы пользователей (генерируются из People Also Ask).
Internal Links – связанные внутренние ссылки (автогенерация).
Schema.org – тип структурированной разметки (Product + AggregateRating).
Images – изображения (автоматическое название файлов).
Reviews – количество отзывов (данные из Trustpilot/Google Reviews).
CTA – призыв к действию («Купить билет», «Забронировать отель»).
Load Speed – скорость загрузки страницы (по данным Lighthouse).
Indexing Priority – приоритет индексации (High, Medium, Low).
Canonical – канонический URL (динамическое заполнение).
Structured Data Type – схема разметки (Product, FAQ, BreadcrumbList).
Cache Control – политика кеширования (max-age=3600, no-cache).
Генерация контента
Контент генерировали через OpenAI API. Использовали разные промты для каждого элемента:
import openai
import pandas as pd

def generate_text(prompt):
    response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7
    )
    return response["choices"][0]["message"]["content"]

df = pd.read_csv("pages.csv")

df["H1"] = df.apply(lambda row: generate_text(f"Создай H1 для билетов {row['Departure City']} – {row['Arrival City']}"), axis=1)
df["Meta Description"] = df.apply(lambda row: generate_text(f"Напиши meta description для страницы авиабилетов {row['Departure City']} – {row['Arrival City']}"), axis=1)
df.to_csv("updated_pages.csv", index=False)
Выгрузка в JSON для загрузки в CMS
import pandas as pd

df = pd.read_csv("updated_pages.csv")
df.to_json("pages.json", orient="records", force_ascii=False)
Как мы автоматизировали индексацию через Indexing API
Сканировали новые URL из базы данных:
import json
import requests

# Загружаем свежие URL для индексации
with open("new_urls.json", "r") as f:
    urls = json.load(f)

# Indexing API Google
API_URL = "https://indexing.googleapis.com/v3/urlNotifications:publish"
HEADERS = {
    "Content-Type": "application/json",
    "Authorization": "Bearer TOKEN"
}

def index_url(url):
    data = {
        "url": url,
        "type": "URL_UPDATED"
    }
    response = requests.post(API_URL, headers=HEADERS, json=data)
    return response.json()

# Запускаем индексацию пачками по 100 URL
for i in range(0, len(urls), 100):
    batch = urls[i:i+100]
    for url in batch:
        result = index_url(url)
        print(result)
Оптимизировали частоту запросов:

  • Использовали random.uniform(1, 3) между запросами, чтобы Google не посчитал нас ботами.
  • Прокидывали индексацию не всех страниц сразу, а с задержкой 3-4 дня для естественного роста.
Верстаем продуктовую страницу: мы сделали ее удобной и продающей
Главное — быстро дать всю нужную информацию. Вверху маршрут, даты и количество путешественников, которые можно менять.
Заголовок, адрес, рейтинг — сразу видно, стоит ли бронировать. Фото крупные, листаются в один клик.

Рейтинг детализирован: не просто звезды, а оценки по категориям. Удобства — иконками, чек-ин и локация — перед глазами.
Страница бронирования отеля GLK PREMIER The Home Suites & Spa: рейтинг, удобства, фотографии номеров и информация о перелете
После отеля логично показать, как до него добраться. Мы добавили авиабилеты в том же стиле, чтобы не выбивались из структуры страницы.

Рядом с заголовком — уточнение, что перелет включен, чтобы снять лишние вопросы. Показываем конкретные рейсы: время вылета, длительность, авиакомпания. Если не подходят — можно сразу изменить.

Дальше — блок с параметрами поездки: маршрут, даты, пассажиры. Кнопка «Изменить» дает возможность быстро поменять параметры, не выходя со страницы.
Блок выбора авиабилетов и параметров поездки: включенные рейсы, даты, количество пассажиров и условия бронирования.
Добавили перелинковку. Сначала — билеты. Человек уже выбрал отель, теперь важно, как он туда попадет. Показываем рейсы, даем возможность поменять даты.

Дальше — достопримечательности. Если пользователь не уверен, что делать в городе, ведем его в раздел с экскурсиями.

Если отель не подошел — показываем альтернативы. Не через блок «похожие варианты», а через аргумент: «Не понравился рейтинг? Вот другие 5-звездочные отели».
Ссылки встроили органично, по логике пользователя. Это увеличивает глубину просмотров и снижает вероятность выхода. Google видит, что страницы связаны, передает им вес, поднимает в поиске.
Рейтинг отеля GLK PREMIER The Home Suites & Spa с оценками по категориям, отзывами туристов и общей информацией о размещении.
Как мы сделали выбор номера полезным для SEO
Обычно страницы отелей устроены так: загружаются скриптом, фильтры работают на фронте, Google ничего не индексирует.

Каждый номер — отдельный блок с H2, уникальными характеристиками и ценой. Заголовки не шаблонные («Double Room»), а с уточнением: «Double 1 или 2 beds deluxe sea view». Это сразу закрывает поисковые запросы вида «Отель + вид из окна».

Разметка Schema.org/Product. В нее передаем:
  • цены и доступность, чтобы Google понимал, что данные актуальны;
  • условия отмены, которые важны для пользователей и увеличивают CTR;
  • варианты питания, чтобы поисковик считал их разными офферами, а не дублями.
Динамическая кнопка «Показать другие номера» открывает список через серверный рендеринг. Это значит, что Google видит все номера и индексирует их как отдельные ссылки, а не скрытые фильтры.

Итог: каждый вариант номера — это точка входа в поиске. Страницы не конкурируют друг с другом, а расширяют охват. Больше контента → выше шанс занять выдачу.
Выбор номеров в отеле: варианты размещения, цены, условия бронирования и возможность гибкой отмены.
Выбор маршрута
Пользователь может менять города, даты и добавлять направления — всё в одном окне.

Но главное — SEO. Каждый новый маршрут генерирует индексируемый URL, а не скрытый параметр.

Если меняют «Амстердам → любой город», сайт создает /flights-from-amsterdam/, что перехватывает низкочастотный трафик.

Кнопка «Добавить направление» усиливает внутреннюю перелинковку. Google видит связанные маршруты, пользователи — больше вариантов.
Форма редактирования маршрута: выбор города отправления, назначения, дат и добавление нового направления.
Популярные маршруты: SEO и перелинковка
Каждый маршрут — это отдельная индексируемая страница. Вместо параметров в URL генерируем /flights/milano-londra/, чтобы Google видел их как полноценные посадочные.

Список обновляется автоматически: алгоритм учитывает частотность запросов, бронирования и клики пользователей. Популярные направления поднимаются выше, слабые уходят вниз.
Список популярных маршрутов: автоматически обновляемые направления с динамическими изображениями и ссылками.
Система раз в сутки пересчитывает рейтинг маршрутов на основе:

  • Частотности запросов (API Serpstat, Google Trends, самописное решение коллег по куки),
  • Бронирований (история продаж),
  • Кликов пользователей (самописное решение коллег).
Формула ранжирования:

Score=(SearchVolume×0.5)+(Bookings×0.3)+(Clicks×0.2)

df = df.sort_values(by="score", ascending=False)
df.to_csv("popular_routes.csv", index=False)
Ссылки формируются в формате /flights/milano-londra/, а изображения подставляются автоматически.

Маршруты всегда актуальны, Google их индексирует, пользователи видят только востребованные направления.
Как отслеживали SEO-результаты
Google Search Console API

SEO-результаты отслеживали автоматически. Данные из Google Search Console собирали через API. Снимали позиции, клики, показы, CTR. Если метрики падали, проверяли, что изменилось в выдаче.

Логи Googlebot анализировали через BigQuery. Искали страницы, которые бот обходит, но не индексирует. Фиксировали частоту сканирования, проверяли, как Google видит сайт.
import requests
import pandas as pd
from google.oauth2 import service_account

# Аутентификация через сервисный аккаунт
SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"]
SERVICE_ACCOUNT_FILE = "service_account.json"

credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES
)

# URL API Google Search Console
GSC_API_URL = "https://www.googleapis.com/webmasters/v3/sites/{site_url}/searchAnalytics/query"

# Параметры запроса
SITE_URL = "sc-domain:example.com"  # Указываем домен
START_DATE = "2024-02-01"
END_DATE = "2024-03-01"
DIMENSIONS = ["query", "page", "country", "device"]

payload = {
    "startDate": START_DATE,
    "endDate": END_DATE,
    "dimensions": DIMENSIONS,
    "rowLimit": 5000
}

# Запрос к API
headers = {"Authorization": f"Bearer {credentials.token}"}
response = requests.post(GSC_API_URL.format(site_url=SITE_URL), json=payload, headers=headers)

if response.status_code == 200:
    data = response.json()["rows"]
    
    # Преобразуем данные в DataFrame
    df = pd.DataFrame(data)
    df["ctr"] = df["clicks"] / df["impressions"]  # Рассчитываем CTR
    
    # Выгружаем данные в CSV
    df.to_csv("gsc_data.csv", index=False)
    print("Данные успешно сохранены.")
else:
    print("Ошибка при запросе к API:", response.text)
Выдачу парсили через Puppeteer. Смотрели, какие сниппеты Google подтягивает, где меняет заголовки и description. Если Google перегенерировал текст — сразу правили шаблоны.
Данные сводили в Looker Studio.

Видели, где проседает, где растет, что нужно править. Если падали позиции, находили причину. Если страницы не индексировались, проверяли технические ошибки.

Всё автоматически. Без ручного мониторинга, без догадок. Только данные, только точные решения.
Результат
Запустили 1 000 000 страниц, развернули полную SEO-структуру.

Трафик: 7,74 млн кликов, 570 млн показов за 16 месяцев. Средняя позиция 9.9, CTR 1.4%.

Изображения дали дополнительный охват: 67,4K кликов, 15,5M показов. График показывает рост после добавления уникальных фото.

Индексация: 245K страниц в Google из 1,1 млн. Остальные подтягиваем через Sitemap, внутренние ссылки и загрузку контента.
SEO-результаты проекта: 7,74 млн кликов, 570 млн показов, 1,4% CTR, 9.9 средняя позиция, 245K страниц в индексе из 1,1 млн.
График индексации страниц: 245K страниц в индексе, 878K не проиндексированы, динамика роста за последние месяцы.

Другие кейсы по SEO и performance продвижению

    Вихров Никита

    Digital-канал
    в России
    №6
    4млн
    Трафика
    ежемесячно
    SEO
    строительство
    в России
    №1
    Изучу проект, посмотрю чем могу быть полезен
    Что нужно сделать?
    Изучу все доступные данные, подготовлю предложение
    Какой ежемесячный бюджет на задачу?
    Нужно, для формирования списка инструментов.
    Ваши контакты для связи
    Напишу в мессенджер
    Продвигал компании