Продвижение бизнеса в интернете
Пригласить в тендер
Закрыть
Выгрузка данных из Яндекс.Директ с указанием условия подбора (корректировки) трафика

Выгрузка данных из Яндекс.Директ с указанием условия подбора (корректировки) трафика

#аналитика
27 июня 6133 просмотра 7 минут на чтение
Зачастую перед аналитиком встает задача глубокого исследования данных о производительности рекламных кампаний в Яндекс.Директе. Результатом такого исследования, как правило, становится отчет или дашборд. Данные, конечно, можно выгружать вручную из интерфейса Яндекс.Директа, но всегда лучше иметь возможность создавать такую выгрузку автоматически. В данной статье мы рассмотрим возможность создания отчета из Яндекс.Директа по E-commerce целям с помощью API Яндекс.Директа.

Узнайте больше про сквозную и предиктивную аналитику. Посмотрите выпуск с руководителем отдела аналитики MediaNation Александром Вахтиным и аналитиком больших данных Романом Святовым: 


Задача: cделать выгрузку из Яндекс.Директ по E-commerce целям с моделью атрибуции “Последний переход из Яндекс.Директа кросс-девайс” и полем “Условие подбора (корректировки)”.

Трудности, с которыми придется столкнуться:

  • Чтобы использовать модель атрибуции “Последний переход из Яндекс.Директа кросс-девайс”, нужно указать ID целей, которые нужно отслеживать. Стандартные цели электронной коммерции не отображаются в списке целей в Яндекс.Директ и Яндекс.Метрика, поэтому из веб-интерфейса ID нужных целей узнать нельзя.
  • Ключевое для нас поле с корректировками можно выгрузить только в типе отчета “Criteria Perfomance Report” или “Custom Report”. Однако в выгрузке будет только ID корректировки, которое само по себе ни о чем не говорит.
  • Чтобы узнать ставку корректировки, необходимо делать отдельный запрос к API по каждой из кампаний.
  • Работать придется одновременно с разными версиями API.

В данной статье не будет затронут вопрос получения токена доступа. Предполагается, что он уже есть. В противном случае, документация к API обладает необходимой инструкцией.
Так же, весь код ниже будет написан на языке Python 3.7, запросы к API будут возвращать JSON.

Возникли трудности при работе с аналитикой? Наши специалисты помогут настроить веб-аналитику
Узнаем ID e-commerce цели
Итак, API Яндекс.Директа требует указать ID цели, чтобы понять, какие конверсии нужно отслеживать. E-commerce цели не отображаются в Яндекс.Метрике, а в Яндекс.Директе указываются без ID. Из-за этого нам нужно запросить список всех целей на аккаунте. Для этого в API Live v.4 существует метод GetRetargetingGoals.

API Live 4 версии устроен таким образом, что все запросы JSON имеют адрес https://api.direct.yandex.ru/live/v4/json/, а нужный метод указывается в теле запроса.

 Общая структура тела запроса:
body = { "method": "GetRetargetingGoals", "param": { "Logins": [ (String), ... ] }, "locale": "ru", "token": token }
Copy
Python
Logins - параметр используется только при выполнении запроса от лица рекламного агентства. В параметре задают массив логинов клиентов (не более 100), для которых требуется получить цели и сегменты.
token - OAuth токен авторизации приложения.
Заголовки можно оставить по умолчанию. Код запроса целиком:
# Адрес сервиса Reports для отправки JSON-запросов (регистрозависимый) ReportsURL = 'https://api.direct.yandex.ru/live/v4/json/' # OAuth-токен пользователя, от имени которого будут выполняться запросы token = 'YOUR_TOKEN' # Логин клиента рекламного агентства # Обязательный параметр, если запросы выполняются от имени рекламного агентства clientLogin = 'CLIENT_LOGIN' # Создание тела запроса body = { "method": "GetRetargetingGoals", "param": { "Logins": [clientLogin] }, "locale": "ru", "token": token } # Кодирование тела запроса в JSON body = json.dumps(body, ensure_ascii=False).encode('utf8') req = requests.post(ReportsURL, data=body) goals_df = pd.DataFrame(req.json()['data'])
Copy
Python

Теперь в датафрейме goals_df находятся все цели, которые есть у clientLogin. Так как рекламодатель может иметь тысячи разных целей на аккаунте, выведем только те, которые относятся к E-commerce:
goals_df[goals_df['Type']=='ecommerce']
Copy
Python
Получим примерно следующий датафрейм:
Login
GoalDomain
Name
Type
GoalID
None
[goalDomain]
[goalDomain] :: сделал покупку
ecommerce
123456789
...
...
...
...
...
Выбираем тот goalDomain, который нам нужен. Теперь, когда у нас есть ID цели, перейдем к составлению отчета.
Создание отчета
Запрос формируется с помощью API 5-ой версии, поэтому имеет ряд отличий.

Во первых, есть настраиваемые заголовки. Во вторых, метод не указывается в теле, а включен в адрес запроса.

Адрес запрос для составления отчета - https://api.direct.yandex.com/json/v5/reports.

Прочитать подробнее:

Полный код выполнения запроса и формирования на основе ответа датафрейма представлен ниже. Не забудьте подставить:
  • ID целей goals_id
  • Токен авторизации token
  • Логин клиента clientLogin

А также изменить название отчета (ReportName в теле запроса в объекте params) по своему усмотрению (важно, чтобы оно было уникальным).
import datetime # Переменная с текущей временной отметкой, # которую мы будем использовать в названии отчета timestamp =str(datetime.datetime.now()).replace(' ','_')# Список с перечислением ID целей, которые нам нужно отслеживать goals_id =[(string),...]# --- Входные данные ---# Адрес сервиса Reports для отправки JSON-запросов (регистрозависимый) ReportsURL ='https://api.direct.yandex.com/json/v5/reports'# OAuth-токен пользователя, от имени которого будут выполняться запросы token ='YOUR TOKEN'# Логин клиента рекламного агентства# Обязательный параметр, если запросы выполняются от имени рекламного агентства clientLogin ='your client login'# --- Подготовка запроса ---# Создание HTTP-заголовков запроса headers ={# OAuth-токен. Использование слова Bearer обязательно"Authorization":"Bearer "+ token,# Логин клиента рекламного агентства"Client-Login": clientLogin,# Язык ответных сообщений"Accept-Language":"ru",# Режим формирования отчета"processingMode":"auto",}# Создание тела запроса body ={"params":{# Временные рамки отчета"SelectionCriteria":{"DateFrom":"2022-03-01","DateTo":"2022-03-22"},# Поля, которые нужно включить в отчет"FieldNames":["Date","CampaignType","CampaignName","CampaignId",'RlAdjustmentId',"Impressions","Clicks","Ctr","Cost","AvgCpc","AvgEffectiveBid","AvgImpressionPosition","AvgTrafficVolume","AvgClickPosition","BounceRate","AvgPageviews","ConversionRate","CostPerConversion","Conversions","GoalsRoi","Revenue","Profit"],# Наши цели"Goals": goals_id,# Название отчета"ReportName": u(f"ReportName_{timestamp}_LYDC"),# Типа отчета"ReportType":"CUSTOM_REPORT",# Уточняем, что используем собственный временной отрезок для отчета"DateRangeType":"CUSTOM_DATE",# Формат получаемых данных (единственный вариант)"Format":"TSV",# Включать ли НДС в денежные суммы в отчете."IncludeVAT":"NO",# Модели атрибуции, используемые при расчете данных по целям Яндекс Метрики.# Несмотря на то, что в документации указано, что по умолчанию используется # модель атрибуции LYDC, на самом деле по умолчанию используется LC"AttributionModels":["LYDC"] }}# Кодирование тела запроса в JSON body = json.dumps(body, indent=4)# --- Запуск цикла для выполнения запросов ---# Если получен HTTP-код 200, то выводится содержание отчета# Если получен HTTP-код 201 или 202, выполняются повторные запросыwhileTrue:try: req = requests.post(ReportsURL, body, headers=headers) req.encoding ='utf-8' # Принудительная обработка ответа в кодировке UTF-8if req.status_code ==400:print("Параметры запроса указаны неверно или достигнут лимит отчетов в очереди")print("RequestId: {}".format(req.headers.get("RequestId",False)))print("JSON-код запроса: {}".format(u(body)))print("JSON-код ответа сервера: \n{}".format(u(req.json())))breakelif req.status_code ==200:print("Отчет создан успешно")print("RequestId: {}".format(req.headers.get("RequestId",False)))print("Содержание отчета: \n{}".format(u(req.text)))breakelif req.status_code ==201:print("Отчет успешно поставлен в очередь в режиме офлайн") retryIn =int(req.headers.get("retryIn",60))print("Повторная отправка запроса через {} секунд".format(retryIn))print("RequestId: {}".format(req.headers.get("RequestId",False))) sleep(retryIn)elif req.status_code ==202:print("Отчет формируется в режиме офлайн") retryIn =int(req.headers.get("retryIn",60))print("Повторная отправка запроса через {} секунд".format(retryIn))print("RequestId: {}".format(req.headers.get("RequestId",False))) sleep(retryIn)elif req.status_code ==500:print("При формировании отчета произошла ошибка. Пожалуйста, попробуйте повторить запрос позднее")print("RequestId: {}".format(req.headers.get("RequestId",False)))print("JSON-код ответа сервера: \n{}".format(u(req.json())))breakelif req.status_code ==502:print("Время формирования отчета превысило серверное ограничение.")print("Пожалуйста, попробуйте изменить параметры запроса - уменьшить период и количество запрашиваемых данных.")print("JSON-код запроса: {}".format(body))print("RequestId: {}".format(req.headers.get("RequestId",False)))print("JSON-код ответа сервера: \n{}".format(u(req.json())))breakelse:print("Произошла непредвиденная ошибка")print("RequestId: {}".format(req.headers.get("RequestId",False)))print("JSON-код запроса: {}".format(body))print("JSON-код ответа сервера: \n{}".format(u(req.json())))break# Обработка ошибки, если не удалось соединиться с сервером API Директаexcept ConnectionError:# В данном случае мы рекомендуем повторить запрос позднееprint("Произошла ошибка соединения с сервером API")# Принудительный выход из циклаbreak# Если возникла какая-либо другая ошибкаexcept:# В данном случае мы рекомендуем проанализировать действия приложенияprint("Произошла непредвиденная ошибка")# Принудительный выход из циклаbreak# Подготавливаем словарь для приведения полей к нужному типу field_types ={'CampaignId':str,'RlAdjustmentId':str,"AvgCpc": np.float64,"Impressions": np.float64,"Clicks": np.float64,"Ctr": np.float64,"Cost": np.float64,"AvgEffectiveBid": np.float64,"AvgImpressionPosition": np.float64,"AvgTrafficVolume": np.float64,"AvgClickPosition": np.float64,"BounceRate": np.float64,"AvgPageviews": np.float64,"Profit": np.float64 }# Проходимся в цикле по всем целямfor goal_id in goals_id: field_types[f'ConversionRate_{goal_id}_LYDC']= np.float64 field_types[f'CostPerConversion_{goal_id}_LYDC']= np.float64 field_types[f'Conversions_{goal_id}_LYDC']= np.float64 field_types[f'GoalsRoi_{goal_id}_LYDC']= np.float64 field_types[f'Revenue_{goal_id}_LYDC']= np.float64 # Формируем DataFrame из полученных данных и заполняем пропуски нулями companies = pd.read_csv(StringIO(req.text), sep='\t', skiprows=1, na_values=('--'), dtype=field_types) companies.fillna('0', inplace=True)
Copy
Python

После выполнения этого кода переменная companies содержит датафрейм с выгрузкой из Яндекс.Директа. Однако, условие подбора представлено только в виде ID. Чтобы получить представление о том, что за корректировка применялась при показе рекламных объявлений, нужно сделать еще один запрос.
Получение названия корректировок
Для получения списка условий ретаргетинга и подбора аудиторий в API v.5 существует сущность RetargetingLists и метод get. Выполнив запрос к адресу https://api.direct.yandex.com/json/v5/retargetinglists, в ответ мы получим список корректировок, где каждому ID будет сопоставлено название условия, которое нам и нужно. Подробнее про этот метод можно прочитать здесь.
headers ={# OAuth-токен. Использование слова Bearer обязательно"Authorization":"Bearer "+ token,# Логин клиента рекламного агентства"Client-Login": clientLogin,# Язык ответных сообщений"Accept-Language":"ru",}# Формируем тело запроса body_segments ={# Необходимо указать, что мы хотим получить данные"method":"get",# В параметрах укажем перечень нужных и информативных полей для выгрузки"params":{"FieldNames":["Type","Id","Name","Description"]}}# Кодирование тела запроса в JSON body_segments = json.dumps(body_segments, indent=4)# Выполнение запроса POST RL_req = requests.post('https://api.direct.yandex.com/json/v5/retargetinglists', body_segments, headers=headers)# Корректировки хранятся в объекте result segments_dict = pd.DataFrame(RL_req.json()['result']['RetargetingLists'])# Меняем тип данных Id на string segments_dict['Id']= segments_dict['Id'].apply(lambda x:str(x))
Copy
Python

Теперь, используя словарь корректировок с ID и их названиями, мы можем добавить в датафрейм соответствующее поле.
defget_rl_name(id):''' Функция для сопоставления Id и выгрузки названия корректиовки. Так как не весь рекламный трафик показывается с корректировками, не всем строкам в отчете получится найти имя для корректировки. Параметры ---------------------------------------------------------------- id : string or int ID корректировки для поиска названия. ''' candidate = segments_dict[segments_dict['Id']==id]if(len(candidate)<1):returnidelse:return candidate['Name'].values[0]# Получение имени корректировки companies['rlName']= companies['RlAdjustmentId'].apply(get_rl_name)
Copy
Python
Пересчет показателей целей
Если при формировании отчета вы указали несколько целей для выгрузки, то API сформировало для каждой из целей свои поля ['ConversionRate', 'CostPerConversion', 'Conversions', 'GoalsRoi', 'Revenue'] в виде {Метрика}_{ID цели}_{Модель атрибуции}, например ConversionRate_23314512_LYDC. Если вам подходит такой формат, вы можете продолжить с ним работать. Если вам нужна общая статистика, то я написал функцию, которую можно использовать для расчета агрегированной статистики.
defcombine_metrics(data, goals_id, method='recalculate', drop=False, weight_label='Conversions'):''' Расчет агрегированной стастики по целям. Параметры ---------------------------------------- data : pandas DataFrame Датафрейм с отчетом из Яндекс.Директа. goals_id : list-like список с ID целей, которые подлежат агрегации method : {'recalculate', 'weighted'}, default 'recalculate' Метод, который используется для расчета метрик ['ConversionRate', 'CostPerConversion', 'GoalsRoi']. При использовании метода 'recalculate' показатели 'Conversions' и 'Revenue' суммируются по целям, остальные метрики рассчитываются по их изначальным формулам с учетом показателей 'Clicks' и 'Cost'. При использовании метода 'weighted' показатели 'Conversions' и 'Revenue' суммируются по целям, остальные метрики рассчитываются как среднее взвешенное по полю, которое указывается в переменной 'weight_label', например, по полю 'Conversions'. В таком случае, количество конверсий будет использоваться в качестве веса цели. drop : bool, default False Если False, оставляет в датафрейме исходные и промежуточные для вычислений поля. Если True, удаляет из датафрейма поля с индивидуальными показателями для целей и промежуточные поля для вычислений. weight_label : single label, default 'Conversions' Название поля, которое используется для рассчета веса цели. Используется, только если method равен 'weighted'. Возвращает ---------------------------------------- DataFrame Датафрейм с агрегированной статистикой '''defcalculate_goals_sums(row, column): _val =0for goal_id in goals_id: goal_column_name =f'{column}_{goal_id}_LYDC' _val +=float(row[goal_column_name])return _val goals_affected_columns =['ConversionRate','CostPerConversion','Conversions','GoalsRoi','Revenue'] data = data.copy()if(method =='recalculate'):for col in['Conversions','Revenue']: data[col]= data.apply(calculate_goals_sums, column=col, axis=1) data['ConversionRate']= data['Conversions']/ data['Clicks'] data['CostPerConversion']= data['Cost']/ data['Conversions'] data['GoalsRoi']=(data['Revenue']- data['Cost'])*100/ data['Cost'] if(method =='weighted'):for col in['Conversions','Revenue']: data[col]= data.apply(calculate_goals_sums, column=col, axis=1)for col in['ConversionRate','CostPerConversion','GoalsRoi']:for goal_id in goals_id: weight =(data[f'{weight_label}_{goal_id}_LYDC']/ data[weight_label]) data[f'{col}_weighted_{goal_id}_LYDC']= data[f'{col}_{goal_id}_LYDC']* weight data[col]= data.apply(calculate_goals_sums, column=f'{col}_weighted', axis=1)if(drop):for goal_id in goals_id:for col in goals_affected_columns: data.drop(columns=[f'{col}_{goal_id}_LYDC'], inplace=True)if(col in['ConversionRate','CostPerConversion','GoalsRoi'])and(method =='weighted'): data.drop(columns=[f'{col}_weighted_{goal_id}_LYDC'], inplace=True) return data
Copy
Python

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

Ниже приведен пример работы функции с параметрами:
method = 'weighted'
drop = True
weight_label = 'Conversions'

Входные данные (синтетические):
Clicks
Cost
Conversions_3002199583_LYDC
Revenue_3002199583_LYDC
ConversionRate_3002199583_LYDC
CostPerConversion_3002199583_LYDC
GoalsRoi_3002199583_LYDC
Conversions_2323131_LYDC
Revenue_2323131_LYDC
ConversionRate_2323131_LYDC
CostPerConversion_2323131_LYDC
GoalsRoi_2323131_LYDC
3070
30700
37
7437
0.090688
101
0.126086
63
30807
0.162477
113
0.557574
2989
14945
97
51604
0.093552
112
0.954095
81
32805
0.075589
109
0.700646

Выходные данные:
Clicks
Cost
Conversions
Revenue
ConversionRate
CostPerConversion
GoalsRoi
3070
30700
100
38244
0.14
108.56
0.40
2989
14945
178
84409
0.09
110.63
0.84

Использовать агрегацию или нет, какой способ агрегации использовать и по какому полю взвешивать цели - вы можете решить сами.
Пример вызова функции:
companies = combine_metrics(companies, goals_id, method='weighted', drop=True, weight_label='Conversions')
Copy
Python
Если вы привыкли работать с отчетом из веб-версии Яндекс.Директа, то вам, возможно, захочется переименовать поля отчета таким же образом, как они выглядят в самом Яндекс.Директе:
companies.rename(columns={'Date':'Дата','CampaignType':'Тип кампании','CampaignName':'Кампания','CampaignId':'№ Кампании','RlAdjustmentId':'RL_ID','Impressions':'Показы','Clicks':'Клики','Ctr':'CTR (%)','Cost':'Расход (руб.)','AvgCpc':'Ср. цена клика (руб.)', 'AvgEffectiveBid':'Ср. ставка за клик (руб.)','AvgImpressionPosition': 'Ср. позиция показов','AvgTrafficVolume': 'Ср. объём трафика','AvgClickPosition':'Ср. позиция кликов','BounceRate':'Отказы (%)','AvgPageviews': 'Глубина (стр.)','Profit':'Прибыль (руб.)','rlName':'Условие подбора (корректировки)','ConversionRate': 'Конверсия (%)','CostPerConversion':'Цена цели (руб.)','Conversions':'Конверсии', 'GoalsRoi':'Рентабельность','Revenue': 'Доход (руб.)'}, inplace=True)
Copy
Python

Так же необходимо привести дату к нужному формату:
companies['Дата']= pd.to_datetime(companies['Дата'],format='%Y-%m-%d')
Copy
Python
На этом моменте выгрузка из API Яндекс.Директа готова к анализу.
Выгрузка величины корректировок
Есть причина, по которой в отчет не стоит включать величину, если вы этого хотите: вы можете получить только текущую величину корректировки. API Яндекс.Директа не позволяет узнать величину корректировку на момент показа объявления.

Однако, если вы знаете, что величина корректировки не менялась, скажем, последний месяц и формируете отчет за последние 30 дней, то смело можете включить данное поле в итоговый датафрейм.

Чтобы получить текущее значение величин корректировок у определенной рекламной кампаний, нужно сделать запрос к API v.5 BidModifiers:get:
headers ={# OAuth-токен. Использование слова Bearer обязательно"Authorization":"Bearer "+ token,# Логин клиента рекламного агентства"Client-Login": clientLogin,# Язык ответных сообщений"Accept-Language":"ru",} body_segments ={# Используется метод 'get'"method":"get","params":{"SelectionCriteria":{ # Идентификаторы РК, по которым нужно получить корректировки"CampaignIds":[(integer),...],# Запрос корректировок на уровне кампаний"Levels":["CAMPAIGN"] },# Имена параметров верхнего уровня, которые требуется получить"FieldNames":["Id","CampaignId","AdGroupId","Level","Type"],# Имена параметров корректировок для целевой аудитории, которые требуется получить"RetargetingAdjustmentFieldNames":["RetargetingConditionId","BidModifier"]}}body_segments = json.dumps(body_segments, indent=4) req = requests.post('https://api.direct.yandex.com/json/v5/bidmodifiers&#39;, body_segments, headers=headers) modifiers_dict = pd.DataFrame(req.json()['result']['BidModifiers']) modifiers_dict["ModifierName"]= modifiers_dict['RetargetingAdjustment'].apply(lambda x: get_rl_name(str(x["RetargetingConditionId"]))) modifiers_dict["ModifierValue"]= modifiers_dict['RetargetingAdjustment'].apply(lambda x: x["BidModifier"])
Copy
Python

Если вывести modifiers_dict[["ModifierName", "ModifierValue"]], то мы получим следующее:
ModifierName
ModifierValue
Название_Корректировки #1
110
Название_Корректировки #2
150
Название_Корректировки #3
70
Название_Корректировки #4
50
Название_Корректировки #5
200

Это все имеющиеся в данный момент корректировки и их значения для рекламной кампании, которая была запрошена.

Надеемся, что данная статья поможет решить ваши задачи.

Не пропустите новые интересные статьи. Подписывайтесь на наш Телеграм-канал и группу ВКонтакте

Другие статьи по теме

30 декабря 2860 просмотров 15 минут
#аналитика#Веб-аналитика
08 октября 4774 просмотра 7 минут
#аналитика
17 сентября 51287 просмотров 7 минут
#аналитика#Веб-аналитика
Что такое веб-аналитика и как настроить веб-аналитику для сайта
Почти все сайты подключены к системам Яндекс.Метрики и Google Analytics. Счетчики установлены, отчеты создаются. Правда, во многих случаях аналитика заканчивается именно на этом этапе. Как организовать действительно эффективную работу по веб-аналитике сайта и сделать ее важной частью бизнеса – в нашем лонгриде.
15 января 16176 просмотров 6 минут
#аналитика
06 октября 8758 просмотров 5 минут
#аналитика#Разработка#Веб-аналитика
Что такое Big Data: как работать с большими данными
Большие данные становятся неотъемлемой частью нашей жизни. Все мы ежедневно используем какие-либо технологии и контактируем с продуктами и крупными компаниями. Компании предлагают нам свои продукты и, в свою очередь, используют данные, которые мы им предоставляем (начиная от отслеживания переходов на сайте и заканчивая персональной информацией при оформлении заказов). Настал момент подробнее разобраться, как собираются эти огромные потоки информации и что с ними делают.
Закрыть
Центральный офис в Москве
8 (495) 215-10-97 Пн-Пт: с 10:00 до 19:00
Наверх