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)