m2_tech блог

Как мы в М2 оценили роль медийной рекламы через связку AdRiver и Яндекс Метрики

Привет! Меня зовут Михаил Васягин, я занимаюсь веб-аналитикой в M2. М2 — это онлайн-платформа для решения вопросов с недвижимостью. Сервисами М2 пользуются как частные лица, так и профессиональные участники рынка — риелторы, застройщики, банки. Мы помогаем тысячам людей экономить время, нервы и деньги. Сейчас каждая пятая квартира в новостройке покупается через нашу платформу.
В 2024 году маркетинг запустил большую медийную кампанию, которая откручивалась с помощью рекламных систем Яндекс Директ и AdRiver. Мы в веб-аналитике не захотели останавливаться на метриках охвата и решили попробовать оценить влияние показов медийной рекламы на дальнейшие источники трафика на сайте.
Таким образом, мы смогли увидеть, какой объем полезных действий на сайте (регистрации и заявки) совершают пользователи, которые контактировали с медийным размещением. Более того, теперь у нас есть возможность оценить uplift-метрики или добавить показы объявлений медийной рекламы в сквозную аналитику как отдельные этапы мультиканальной атрибуции.
Для решения первичной задачи мы использовали сырые данные AdRiver (post-view) и Яндекс Метрики (post-click), а также базу данных ClickHouse и Apache Superset для визуализации.
Материал будет полезен веб и маркетинговым аналитикам, а также маркетологам, кто хочет самостоятельно разобраться в сырых данных AdRiver и Яндекс Метрики. А именно: как их можно связывать между собой, какие отчеты построить на этих данных и какие полезные выводы для бизнеса можно сделать.

Синхронизация AdRiver и Яндекс Метрики: план, которого мы придерживались

Основная проблема состоит в том, что данные лежат в разных аналитических системах. Непонятно, насколько корректно сработает синхронизация cookie между AdRiver и Яндекс Метрикой — какой именно будет процент совпадений между двумя разными аналитическими системами. Еще мы не смогли найти примеры практических материалов с похожими кейсами, поэтому и решились написать эту статью.
После продолжительного ресерча мы сделали вывод, что наиболее понятная и детальная теория есть в материалах ресурса CyberBrain. Затем мы решили двигаться по шагам из этой статьи. Вот наш план:
  1. Устанавливаем на сайт счетчик AdRiver*
  2. Получаем сырые данные от AdRiver и загружаем их в ClickHouse
  3. Сверяем синхронизацию cookie между AdRiver и Яндекс Метрикой
  4. Пишем витрину данных, определяемся с типом атрибуции, а также выбираем необходимое окно атрибуции
  5. Строим дашборд в Superset, в котором базово будет видно.
  • какой процент пользователей из тех, кто видел объявление медийной рекламы, посещал сайт;
  • какой процент пользователей из тех, кто посещал сайт, до этого видел объявление медийной рекламы.
6. Анализируем дашборд и ищем инсайты
*Медийные кампании должны размещаться именно в Adtracker-системах или быть размечены специальными метками на показ и клик этих систем. Причина в том, что, например, Яндекс Метрика для медийной рекламы не дает возможность выгрузить сырые данные по показам объявлений, поэтому про оценку кампаний, запущенных с помощью Яндекс Директ, в статье речь не пойдет.
Здесь стоит добавить, что для текущей задачи необходимым также является экспорт сырых данных Яндекс Метрики в ClickHouse, стандартизированная UTM-разметка, а также наличие в Яндекс Метрике ключевых размеченных событий на сайте (про дальнейшее склеивание Яндекс Метрики с CRM системой в статье речь не пойдет).

Как мы установили счетчик AdRiver и выгрузили данные в ClickHouse

Устанавливаем на сайт счетчик AdRiver
На первый взгляд все просто: нам нужен счетчик counter, получить который можно по инструкции. Его необходимо установить на все страницы сайта. Если возникнут проблемы, например, в процессе установки счетчика через Google Tag Manager, он может не пропустить стандартный js-код, можно написать в поддержку AdRiver с просьбой предоставить счетчик для сайта. С помощью него можно будет связать данные post-view и post-click.
Важно отметить, что счетчик AdRiver забирает first-party cookie Яндекс Метрики, поэтому он должен срабатывать именно после инициализации счетчика Яндекс Метрики. Это можно, например, настроить с помощью последовательности активации тегов в Google Tag Manager. В случае успешной установки при загрузке страницы сайта в консоли разработчика на вкладке Network должна появиться такая строчка:
Важное сообщение
Лучше протестировать сбор данных в различных браузерах в режиме инкогнито. В Request URL обязательно должен быть параметр yid1, значение которого мы увидим в выгрузке AdRiver по сайту в следующем шаге.

Получаем сырые данные от AdRiver и загружаем их в ClickHouse

Выгрузка логов у AdRiver платная, но стоимость небольшая и рассчитывается по фиксированной ставке за CPM. Данные выгружаются ежедневно за вчерашний день на выделенный вам FTP-сервер и хранятся 7 дней. Можно также попросить выгрузить исторические данные за нужный диапазон дат. Забрать эти данные к себе локально можно с помощью библиотеки requests на python, подставив свои урлы, даты и креды:
import pandas as pd
import requests
from requests.auth import HTTPBasicAuth
import gzip
from io import BytesIO

generate_dates = pd.date_range('2024-01-01', periods=30).tolist()

datelist = []
for date in generate_dates:
    date = date.strftime('%Y-%m-%d')
    datelist.append(date)
print(datelist)

for date in datelist:
    res = requests.get(f"https://files.adriver.ru/your_login.ftp/{date}.logsite.csv.gz", 
                       auth=HTTPBasicAuth('your_login', 'your_password'))
    compressed_content = res.content
    compressed_data = BytesIO(compressed_content)

    with gzip.GzipFile(fileobj=compressed_data, mode='rb') as decompressed_data:
        decompressed_content = decompressed_data.read()

    with open('files/example.csv', 'wb') as f: 
        f.write(decompressed_content)
Полученные файлы загружаем в ClickHouse с помощью python, например, используя библиотеку SQLAlchemy. При больших объемах можно разбивать данные на чанки.
Получаем две таблицы – ADRIVER_AD, взаимодействия пользователей с рекламными объявлениями – показы, клики, etc., и ADRIVER_SITE, посещения пользователями сайта, на который мы установили счетчик в первом шаге. Есть подробное описание полей в документации. Их достаточно много и все можно использовать для различных срезов, но необходимым минимумом для нашей задачи из таблицы ADRIVER_AD будут:
  • user_id
  • user_ip
  • user_agent
  • datetime
  • type
  • banner_id
  • domain
  • utm_source / utm_medium / utm_campaign / utm_content
Из таблицы ADRVIER_SITE:
  • user_id
  • yandex_id_first
  • datetime
Сверяем синхронизацию cookie между AdRiver и Яндекс Метрикой
Так как мэтчинг данных в финальной витрине будет происходить по полю yandex_id_first из таблицы ADRIVER_SITE и полю ym:s:clientID (ниже client_id_ym) из таблицы визитов Logs API Яндекс Метрики, нам нужно понять примерный процент потерь first-party cookie, которые записывает Яндекс Метрика, но по каким-то причинам не записывает AdRiver. На текущий момент у нас процент совпадений составляет в среднем 90% за день. Но начинали мы с 60%... Для себя мы выявили несколько причин, почему могут быть большие расхождения:
  • Счетчик AdRiver срабатывает раньше, чем инициализируется счетчик Яндекс Метрики. В таком случае в поле yandex_id_first таблицы ADRIVER_SITE будут пустые значения, решение проблемы описано в первом шаге
  • На сайте установлено много пикселей других рекламных или аналитических систем на один и тот же триггер в Google Tag Manager. Они могут мешать корректному сбору данных счетчиком AdRiver, поэтому можно попробовать подвинуть активацию других тегов на 1-2 секунды
  • Различные ограничения браузеров, отсюда потери в 10%, они приемлемы
Также мы заметили, что большие потери присутствуют в сегменте именно новых пользователей Яндекс Метрики, а по устройствам и браузерам выбросов у нас не было.

Как мы написали витрину данных: атрибуция и моделирование

У медийной кампании было несколько этапов, первый был одинаковый для всех пользователей. Нам было интересно посмотреть, после показа какого этапа пользователи совершают сессии на сайте, поэтому мы выбрали атрибуцию по last-view, последнему показанному объявлению, и окно атрибуции в 90 дней. Таким образом, показ объявления будет отнесен к сессии, а также ко всем последующим сессиям пользователя на сайте только в том случае, если между ними не прошло 90 календарных дней.
Финальная витрина имеет следующую модель данных. Ниже будут примеры кода для формирования промежуточных таблиц.
Начинаем работу над витриной с данных ADRIVER_AD и сформируем таблицу “ad_group_table” с уникальными user_id и отсортированным массивом событий для дальнейшей атрибуции сессий пользователя к последнему показанному объявлению по event_datetime. Здесь мы объявляем уникальную комбинацию event_date, banner_id, domain (то есть в один день уникальный баннер мог показываться на одной площадке несколько раз). Также для user_id = 0, таких в наших данных было около 30% строк, мы проставляем искусственный идентификатор, который представляет собой хэш-функцию user_ip и user_agent, чтобы попробовать идентифицировать ненайденных пользователей для более точного дальнейшего анализа:
with ad_group_table as (
select user_id_ad_group,
       groupArray(event_datetime) as group_event_datetime,
       groupArray(event_date) as group_event_date,
       groupArray(banner_id) as group_banner_id,
       groupArray(domain) as group_domain
from (
    select if(user_id = '0', hex(SHA1(concat(user_ip, user_agent))), user_id) as user_id_ad_group,
           datetime as event_datetime,
           date as event_date,
           banner_id,
           domain
    from ADRIVER_AD
    order by all)
group by user_id_ad_group),
В финальной витрине ко всем объявлениям проставляются необходимые атрибуты (media_utm_medium у таких строк будет иметь значение ‘cpm’), показы и клики из таблицы “ad_banner_table”. А также с помощью full join мы подтягиваем показы и клики по всем остальным ненайденным пользователям. Так как показ одного объявления может быть отнесен ко многим сессиям, то применим оконную функцию rn_ad_banner для корректного подсчета на дашборде показов и кликов по объявлениям:
Поле
Строка 1
Строка 2
Строка 3
date
2025-01-31
2025-01-31
1970-01-01
session_dt
2025-01-31 23:57:35
2025-01-31 23:55:50
1970-01-01 03:00:00

Что в итоге

Подводя итог по дашборду, могу сказать, что это лишь малая часть того, что мы можем проанализировать по финальной витрине. Например, здесь также напрашивается:
  • Расчёт uplift, как разницы conversion rate в полезные действия между контактировавшими и не контактировавшими с медийной рекламой пользователями
  • Расчет медианного количества времени от показа объявления медийной рекламы до совершения сессии или полезного действия на сайте
Я считаю, что построение такой финальной витрины — хороший старт для общего ознакомления с post-view данными. Следующими шагами можно добавлять показы объявлений медийной рекламы в сквозную аналитику как отдельные этапы мультиканальной атрибуции.
Надеюсь, данная статья поможет открыть завесу тайны по вопросу работы с сырыми post-view данными. Текущий вариант — лишь одна из интерпретаций, которых может быть множество, и это замечательно. Буду рад обсудить детали реализации и ответить на ваши вопросы в комментариях. Расскажите, приходилось ли вам самостоятельно работать с сырыми post-view данными? Какие специалисты принимали участие в реализации и каких результатов удалось достичь? С какими трудностями столкнулись?
2025-06-03 16:05