В данной статье я расскажу про то, как следить за рекламными аккаунтами с помощью API и Python.
На выходе мы получим запрос к API Яндекс Директ, с помощью которого будем получать статистику по рекламным кампаниям и сможем обрабатывать эти данные.
Для этого нам нужно:
- Получить токен API Яндекс Директ
- Написать запрос к серверу
- Импортировать данные в DataFrame
Импорт библиотек
Необходимо импортировать те библиотеки, которые используются в запросе, а также «pandas» и «DataFrame».
Весь импорт будет выглядеть так:
import requests from requests.exceptions import ConnectionError from time import sleep import json import pandas as pd import numpy as np from pandas import Series,DataFrame
Получение токена
Данный момент я не смогу рассказать лучше документации API Директа, поэтому оставлю ссылку.
(Инструкция по получению токена)
Пишем запрос к серверу API Яндекс Директ
Копируем запрос из документации API
Изменим запрос.
- Прописываем свои токен и логин
Токен.
token = ‘blaBlaBLAblaBLABLABLAblabla’
Логин.
clientLogin = 'e-66666666'
- Подстраиваем тело запроса под себя.
Из этого
body = { "params": { "SelectionCriteria": { "DateFrom": "НАЧАЛЬНАЯ_ДАТА", "DateTo": "КОНЕЧНАЯ_ДАТА" }, "FieldNames": [ "Date", "CampaignName", "LocationOfPresenceName", "Impressions", "Clicks", "Cost" ], "ReportName": u("НАЗВАНИЕ_ОТЧЕТА"), "ReportType": "CAMPAIGN_PERFORMANCE_REPORT", "DateRangeType": "CUSTOM_DATE", "Format": "TSV", "IncludeVAT": "NO", "IncludeDiscount": "NO"
Делаем это
body = { "params": { "SelectionCriteria": { "Filter": [ { "Field": "Clicks", "Operator": "GREATER_THAN", "Values": [ "0" ] }, ] }, "FieldNames": [ "CampaignName", "Impressions", "Clicks", "Ctr", "Cost", "AvgCpc", "BounceRate", "AvgPageviews", "ConversionRate", "CostPerConversion", "Conversions" ], "ReportName": u("Report4"), "ReportType": « ", "DateRangeType": "LAST_5_DAYS", "Format": "TSV", "IncludeVAT": "NO", "IncludeDiscount": "NO" } }
В SelectionCriteria пишем, как будем отбирать данные. Стандартно там пишется 2 даты, но, чтобы не нужно было их постоянно менять, заменим отрезок времени на «Последние 5 дней».
Устанавливаем фильтр для данных. Это в первую очередь требуется для того, чтобы не получать пустых значений. Проблема в том, что Директ показывает отсутствующие данные как два минуса, из-за чего меняется тип данных у всего столбца, после чего вы не сможете проводить математические операции без лишних телодвижений.
FieldNames. Прописываем тут данные, которые вам необходимы. Я прописал поля, которые использую для анализа, у вас список может отличаться.
ReportType. В данном поле пишется тип отчета, для кампаний нужен именно этот отчет.
У вас должно получиться что-то подобное.

5. Импортируем данные в DataFrame.
(DataFrame, вероятно, самый подходящий способ для работы с этими данными.)
Я смог реализовать эту функцию с помощью записи и чтения csv файла.
Находим в запросе кусок, который отвечает за вывод статистики — это «req.text».
Удаляем стандартный вывод программы на запись в файл. Для этого меняем все выводы в коде 200.
print("Отчет создан успешно") print("RequestId: {}".format(req.headers.get("RequestId", False))) print("Содержание отчета: \n{}».format(u(req.text)))
На:
format(u(req.text))
Теперь импортируем ответ сервера в DataFrame.
file = open("cashe.csv", "w") file.write(req.text) file.close() f = DataFrame.from_csv("cashe.csv",header=1, sep=' ', index_col=0,)
Пошагово:
- Открываем (и автоматически создаем) файл cashe.csv для записи
- Записываем в него ответ сервера
- Закрываем файл
- Открываем файл как DataFrame (указываем, название файла, в какой строке находятся заголовки таблицы, какой делитель между данными, в каком столбце индекс)
Получилось следующее:

Убираем ограничение на вывод столбцов:
pd.set_option('display.max_columns', None) pd.set_option('display.expand_frame_repr', False) pd.set_option('max_colwidth', -1)
Теперь показывается все:

Единственная проблема — денежные значение показываются не так, как хотелось. Это особенности реализации API Яндекс Директ. Нам просто нужно разделить денежные значения на 1000000.
f['Cost'] = f['Cost']/1000000 f['AvgCpc'] = f['AvgCpc']/1000000 f['CostPerConversion'] = f['CostPerConversion']/1000000
Также предлагаю сразу сделать сортировку по количеству кликов
f=f.sort_values(by=['Clicks'], ascending=False)
Вот у нас и получился готовый к анализу DataFrame

Я для себя написал подобные запросы для получения статистики в разрезе дней и в разрезе кампаний, чтобы всегда быть в курсе отклонений трафика и понимать где примерно произошло отклонение.
Спасибо за внимание.
import requests from requests.exceptions import ConnectionError from time import sleep import json import pandas as pd import numpy as np from pandas import Series,DataFrame pd.set_option('display.max_columns', None) pd.set_option('display.expand_frame_repr', False) pd.set_option('max_colwidth', -1) # Метод для корректной обработки строк в кодировке UTF-8 как в Python 3, так и в Python 2 import sys if sys.version_info < (3,): def u(x): try: return x.encode("utf8") except UnicodeDecodeError: return x else: def u(x): if type(x) == type(b''): return x.decode('utf8') else: return x # --- Входные данные --- # Адрес сервиса Reports для отправки JSON-запросов (регистрозависимый) ReportsURL = 'https://api.direct.yandex.com/json/v5/reports' # OAuth-токен пользователя, от имени которого будут выполняться запросы token = 'тут токен' # Логин клиента рекламного агентства # Обязательный параметр, если запросы выполняются от имени рекламного агентства clientLogin = 'тут логин' # --- Подготовка запроса --- # Создание HTTP-заголовков запроса headers = { # OAuth-токен. Использование слова Bearer обязательно "Authorization": "Bearer " + token, # Логин клиента рекламного агентства "Client-Login": clientLogin, # Язык ответных сообщений "Accept-Language": "ru", # Режим формирования отчета "processingMode": "auto" # Формат денежных значений в отчете # "returnMoneyInMicros": "false", # Не выводить в отчете строку с названием отчета и диапазоном дат # "skipReportHeader": "true", # Не выводить в отчете строку с названиями полей # "skipColumnHeader": "true", # Не выводить в отчете строку с количеством строк статистики # "skipReportSummary": "true" } # Создание тела запроса body = { "params": { "SelectionCriteria": { "Filter": [ { "Field": "Clicks", "Operator": "GREATER_THAN", "Values": [ "0" ] }, ] }, "FieldNames": [ "CampaignName", "Impressions", "Clicks", "Ctr", "Cost", "AvgCpc", "BounceRate", "AvgPageviews", "ConversionRate", "CostPerConversion", "Conversions" ], "ReportName": u("Report4"), "ReportType": "CAMPAIGN_PERFORMANCE_REPORT", "DateRangeType": "LAST_5_DAYS", "Format": "TSV", "IncludeVAT": "NO", "IncludeDiscount": "NO" } } # Кодирование тела запроса в JSON body = json.dumps(body, indent=4) # --- Запуск цикла для выполнения запросов --- # Если получен HTTP-код 200, то выводится содержание отчета # Если получен HTTP-код 201 или 202, выполняются повторные запросы while True: try: req = requests.post(ReportsURL, body, headers=headers) req.encoding = 'utf-8' # Принудительная обработка ответа в кодировке UTF-8 if 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()))) break elif req.status_code == 200: format(u(req.text)) break elif 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()))) break elif 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()))) break else: 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 file = open("cashe.csv", "w") file.write(req.text) file.close() f = DataFrame.from_csv("cashe.csv",header=1, sep=' ', index_col=0,) f['Cost'] = f['Cost']/1000000 f['AvgCpc'] = f['AvgCpc']/1000000 f['CostPerConversion'] = f['CostPerConversion']/1000000 f=f.sort_values(by=['Clicks'], ascending=False) print(f)

