Работая сразу с несколькими клиентами, появляется необходимость оперативно анализировать много информации в разных аккаунтах и отчетах. Когда клиентов становится больше 10, маркетолог больше не успевает постоянно следить за статистикой. Но выход есть.
В данной статье я расскажу про то, как следить за рекламными аккаунтами с помощью API и Python.
На выходе мы получим запрос к API Яндекс Директ, с помощью которого будем получать статистику по рекламным кампаниям и сможем обрабатывать эти данные.
Для этого нам нужно:
Необходимо импортировать те библиотеки, которые используются в запросе, а также «pandas» и «DataFrame».
Весь импорт будет выглядеть так:
Данный момент я не смогу рассказать лучше документации API Директа, поэтому оставлю ссылку.
(Инструкция по получению токена)
Копируем запрос из документации API
Изменим запрос.
Токен.
token = ‘blaBlaBLAblaBLABLABLAblabla’
Логин.
clientLogin = 'e-66666666'
Из этого
Делаем это
В SelectionCriteria пишем, как будем отбирать данные. Стандартно там пишется 2 даты, но, чтобы не нужно было их постоянно менять, заменим отрезок времени на «Последние 5 дней».
Устанавливаем фильтр для данных. Это в первую очередь требуется для того, чтобы не получать пустых значений. Проблема в том, что Директ показывает отсутствующие данные как два минуса, из-за чего меняется тип данных у всего столбца, после чего вы не сможете проводить математические операции без лишних телодвижений.
FieldNames. Прописываем тут данные, которые вам необходимы. Я прописал поля, которые использую для анализа, у вас список может отличаться.
ReportType. В данном поле пишется тип отчета, для кампаний нужен именно этот отчет.
У вас должно получиться что-то подобное.

5. Импортируем данные в DataFrame.
(DataFrame, вероятно, самый подходящий способ для работы с этими данными.)
Я смог реализовать эту функцию с помощью записи и чтения csv файла.
Находим в запросе кусок, который отвечает за вывод статистики — это «req.text».
Удаляем стандартный вывод программы на запись в файл. Для этого меняем все выводы в коде 200.
На:
Теперь импортируем ответ сервера в DataFrame.
Пошагово:
Получилось следующее:

Убираем ограничение на вывод столбцов:
Теперь показывается все:

Единственная проблема — денежные значение показываются не так, как хотелось. Это особенности реализации API Яндекс Директ. Нам просто нужно разделить денежные значения на 1000000.
Также предлагаю сразу сделать сортировку по количеству кликов
Вот у нас и получился готовый к анализу DataFrame

Я для себя написал подобные запросы для получения статистики в разрезе дней и в разрезе кампаний, чтобы всегда быть в курсе отклонений трафика и понимать где примерно произошло отклонение.
Спасибо за внимание.
В данной статье я расскажу про то, как следить за рекламными аккаунтами с помощью 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)
