Задача исследования поискового спроса знакома каждому SEO-специалисту. Сбор и кластеризация семантики, анализ популярности (частотности) и сезонности запросов не вызывает каких-либо особых сложностей. Но что, если нужно исследовать целую сферу бизнеса, в которой представлено более 50 брендов, а сроки и ресурсы ограничены? Именно эту задачу мы и попробуем решить.
Если вы следите за активностью JetStyle (агентства, где я работаю) – то могли заметить, что мы активно исследуем автомобильный рынок. Он бурно меняется, одни бренды ушли, а на их место приходит множество других, Китайских. Подробнее про исследование и плакат, который у нас получился читайте тут.
Так вот, нам захотелось понять что происходит со спросом на автомобили различных брендов — кто упал, а кто вырос. Отсюда можно сформулировать нашу задачу.
Задача и инструментарий
Задача: Исследовать динамику спроса на автомобильные бренды в России.
Кажется, вполне себе обычная задача, знакомая каждому SEO-специалисту: собрать поисковые запросы, собрать сезонности из Яндекс Wordstat, визуализировать собранные данные.
Из задачи вытекает необходимый инструментарий:
Базовый:
- KeyCollector: с его помощью мы сможем легко и быстро собрать запросы и сезонности. К нему понадобится также аккаунт Яндекс Директ и proxy;
- Google-таблицы: в них мы можем сложить результат и там же визуализировать, создав простенькие графики;
Продвинутый:
- Python и библиотека Pandas: поможет автоматизировать некоторые рутинные задачи;
- ClickHouse: место, куда можно удобно сложить собранные данные. Можно остаться и в Google-таблицах, просто это надежнее и с заделом на будущее;
- Yandex Data Lens: Bi-инструмент от Яндекса, в котором мы сможем построить продвинутую визуализацию наших данных;
Решение
Способ 1 — решаем «В лоб»
Для начала попробуем решить задачу «в лоб». Составляем список всех автомобильных брендов, представленных на рынке. Не забываем, что нам нужны несколько вариантов названия — на английском и на русском. Обычно вариант на русском более популярен у пользователей. Помимо этого могут быть словоформы русского названия — к «европейцам» все привыкли, но с «китайцами» сложнее, один только новый китайский бренд KAIYI чего стоит.
Для этого используем логику и поисковые подсказки Яндекса, а Яндекс Вордстат используем для проверки является ли словоформа используемой пользователями.

В результате получаем таблицу со списком автомобильных брендов:

Далее загружаем наши фразы в KeyCollector и собираем сезонности по России. Описывать здесь я это не буду, есть отличное видео у Александра Ожгибесова про настройку KeyCollector:
С парсингом сезонностей там тоже ничего сложного: выбираем регион, запускаем процесс и занимаемся другими делами пока данные собираются.

Когда мы собрали сезонности — экспортируем их. На выходе KeyCollector выдает нам CSV со списком запросов и популярностью по месяцам. Загрузим его в Google Sheets. Для простой визуализации этого может оказаться достаточно, но мы немного усложним задачу в угоду функциональности.
Шаг 1. Трансформируем таблицу в вертикально-ориентированную, здесь можно сделать это и вручную т.к. данных не очень много.
Шаг 2. Обогатим данными (добавим название бренда для разных словоформ и страну бренда).

Шаг 3. Подключим Google Sheets к Yandex Data Lens для более удобного построения чартов.



Строим первый чарт и…….получаем ничего.





Все дело в том что в этом решении мы не учитываем интент поискового запроса, а смотрим просто все бренды в общем. В результате картина смазанная и не видно наглядно динамики по вытеснению европейцев, корейцев и японцев китайцами, хотя казалось бы она очевидна из-за того что мы видим на улицах и в рекламе.
Способ 2 – если копнуть чуть глубже
Возвращаемся на шаг сбора поисковых фраз. Вспоминаем что у нас уже есть список всех брендов. Надо добавить к ним поисковый интент. Для этого пойдем в SpyWords (он кстати доступен бесплатно, если вы используете Elama и у вас тариф оптимум) и посмотрим по каким группам фраз находятся в поиске официальные сайты нескольких брендов.


Выгрузим отчеты по этим сайтам и внимательно изучим их.

Совет: Эту же задачу можно решить выгрузив все фразы по одному бренду из Yandex Wordstat, кластеризовав их с помощью Разбиваки Алексея Кулакова и проанализировав получившиеся группы.
Update: После закрытия Yandex XML сервис стал платным, можно воспользоваться им или любым другим сервисом кластеризации, например OverLead.

Получаем список поисковых интентов:
- официальный сайт
- официальный дилер
- модельный ряд
- комплектации
- цена
- купить
- кредит
- лизинг
- рассрочка
- отзывы
- обзор
- тест драйв
- сервис
Добавив их к нашим брендам мы сможем получить «оценку сверху» по всем кластерам запросов для каждого бренда.
Добавить их можно вручную – записываем все бренды в первую колонку, а интенты в первую строку таблицы и перемножив. Но, для того чтобы затем получить список всех брендов в табличном виде придется проделать много монотонной ручной работы:

Эту задачу гораздо проще решить с помощью простого цикла на Python и записать обратно в Google Sheets с помощью библиотеки gspread.
# Импортируем библиотеки
import pandas as pd
import gspread
# Google Sheets Credentials
gc = gspread.service_account(filename='./credentials/filename.json')
# ID документа в Google Sheets
G_SHEET = '1C2ol60IPxUyeZgQdsxDNW5bs48PS6YcCJNL-TpCkvDA'
# Название или ID листа в Google Sheets
G_PAGE_ID = 'KW List'
# Открываем нашу Google-таблицу и забираем оттуда данные
sh = gc.open_by_key(G_SHEET)
worksheet = sh.worksheet(G_PAGE_ID)
list_of_lists = worksheet.get_all_values()
# Переводим их в DataFrame и обрабатываем его
df = pd.DataFrame(list_of_lists[1:])
df.columns = ['brand', 'keyword', 'word']
# Смотрим что получили
df

# Формируем список брендов
brands_list = []
for brand in df.keyword:
for word in df.word[0:13]:
brands_list.append(brand + ' ' + word)
brands_df = pd.DataFrame(brands_list)
brands_df

Теперь вместо 100+ запросов у нас получилось 2000+ запросов без лишнего сбора и кластеризации семантики. Загружаем их в KeyCollector и также парсим сезонности. На этот раз подождать придется подольше (у меня ушло примерно 4-5 часов на сбор данных, я просто оставил Mac с запущенной виртуальной машиной на ночь). Также я дополнительно разложил все фразы на группы для удобства, т.к. тогда при выгрузке группа добавится в отдельную колонку, что позволит объединить словоформы в группы по брендам. (Эту же задачу можно решить при обработке на Python, см. примеры далее по тексту).
На выходе снова получаем CSV с данными о сезонности. И вот теперь для пред-обработки этих данных точно нужно использовать Python, поскольку вручную это будет очень долго.
Загружаем наши данные в Data Frame, переводим таблицу в вертикально-ориентированную при помощи метода .melt
# Считываем данные из CSV файла, полученного из KeyCollector
df = pd.read_csv('cache/season_data.csv', sep = ';')
# Переименовываем колонки
df = df.rename(columns={"Фраза": "Keyword", "Родительская группа": "Brand"})
# Удаляем пустую колонку, которая была в выгрузке
df = df.drop(columns=['Unnamed: 26'])

# Преобразуем таблицу в вертикально-ориентированную
stacked_df = pd.melt(df, id_vars=['Brand', 'Keyword'])
# Переименовываем колонки
stacked_df = stacked_df.rename(columns={"variable": "Date", "value": "WordstatPopularity"})

Далее нам надо привести даты в формат yyyy-mm-dd, делаем это при помощи следующего метода:
# Добавим столбец Month с датой в нужном формате
stacked_df['Month'] = pd.to_datetime(stacked_df['Date'], dayfirst=True)
Затем мы хотим обогатить наши данные.
Для добавления поискового интента к фразе (что позволит например объединить фразы «… цена» и «…купить» в одну группу) составим список соответствий: «Часть фразы — интент»
intents_dict = {
'.*официальный сайт.*': 'Официальный сайт или дилер',
'.*официальный дилер.*': 'Официальный сайт или дилер',
'.*модельный ряд.*': 'Комплектации и модели',
'.*комплектации.*': 'Комплектации и модели',
'.*цена.*': 'Покупка',
'.*купить.*': 'Покупка',
'.*кредит.*': 'Кредит или лизинг',
'.*лизинг.*': 'Кредит или лизинг',
'.*рассрочка.*': 'Кредит или лизинг',
'.*отзывы.*': 'Обзоры',
'.*обзор.*': 'Обзоры',
'.*тест драйв.*': 'Обзоры',
'.*сервис.*': 'Обслуживание'
}
И обработаем наш дата-фрейм с помощью метода .replace. Как видим, в отдельную колонку у нас добавился поисковый интент.
stacked_df['SearchIntent'] = stacked_df['Keyword'].replace(regex=intents_dict)

Для добавления страны бренда ко всем запросам составим словарь соответствий бренд — страна и обработаем наш дата-фрейм с помощью метода .map
# Заведем словарь стран
brands_dict_ru = {
'Avatr': 'Китай',
'Baic':'Китай',
'Brilliance': 'Китай',
# ...
'Subaru': 'Япония',
'Suzuki': 'Япония',
'Toyota': 'Япония',
'N/A': 'N/A'
}
# Добавим колонку со странами на основе названий брендов
stacked_df['BrandCountry'] = stacked_df['Brand'].map(brands_dict_ru)
# Удалим не нужную нам колонку Date
stacked_df = stacked_df.drop(columns=['Date'])
В моем примере в дата-фрейме уже есть список всех брендов, поскольку я разбил фразы на группы в интерфейсе KeyCollector, однако эту же задачу можно решить с помощью Python по аналогии с обработкой поисковых интентов, описанной выше.

Итак, у нас получилась таблица следующего формата:
- Месяц (
Month) - Страна (
BrandCountry) - Бренд (
Brand) - Интент запроса (
SearchIntent) - Поисковый запрос (
Keyword) - Популярность запроса (
WordstatPopularity)
Мы можем сохранить её в CSV, Google Sheets или ClickHouse. Варианты работы с CSV и Google Sheets неплохо описаны, я буду делать через ClickHouse.
Понадобится подключиться к базе (я использую код из справки Yandex Cloud, ссылка). Там уже написаны функции для записи и чтения. (Единственное что пришлось доработать – убрать проверку SSL-сертификата, поскольку я развернул свой ClickHouse не в Yandex Cloud и не стал настраивать SSL).

Далее удаляем таблицу если она уже создана. Создаем её заново, указав при этом типы данных. Записываем туда наш DataFrame.
# Задаем реквизиты для подключения
# IP-адрес и хост
CH_HOST_NAME = '***.***.***.***'
CH_HOST = f'http://{CH_HOST_NAME}:8123'
# Имя пользователя, базы и таблицы
CH_USER = 'user_name'
CH_DB_NAME = 'db_name'
CH_DB_TABLE = 'database_name'
# Забираем пароль из файла с реквизитами
CH_PASS = open('credentials/password.txt').read().strip()
# Создаем подключение
my_client = simple_ch_client(CH_HOST, CH_USER, CH_PASS)
# Проверяем подключение через получение версии базы данных
my_client.get_version()
# Удаляем таблицу если она есть
q = f'drop table if exists {CH_DB_NAME}.{CH_DB_TABLE} '
my_client.get_clickhouse_data(q)
# Создаем таблицу
q = f'''
create table {CH_DB_NAME}.{CH_DB_TABLE} (
Month Date,
Brand String,
SearchIntent String,
Keyword String,
BrandCountry String,
WordstatPopularity Float32
) ENGINE = MergeTree() ORDER BY (Month) SETTINGS index_granularity=8192
'''
my_client.get_clickhouse_data(q)
# Загружаем DataFrame
my_client.upload(
f'{CH_DB_NAME}.{CH_DB_TABLE}',
stacked_df.to_csv(sep='\t', index =False))
Смотрим, что получилось в базе данных.

Теперь используя Data Lens подключаемся к нашей базе и строим необходимые чарты.



Собранные чарты собираем в дашборд, добавляем туда фильтры.
Результат
- Мы построили наглядную визуализацию того, что происходит со спросом на автомобильном рынке в разрезе по бренду, стране бренда и поисковому интенту в динамике;
- Мы сэкономили время на рутинных операциях: составления списка ключевых слов с помощью перемножения двух списков, подготовке и предобработке данных для визуализации;
- Мы немного прокачались в Python;
- И самое главное — мы видим что происходит со спросом.