Получение статистики по всем клиентам из API Яндекс Директ в разрезе дней с помощью Python

  • Tutorial
В работе часто использую короткую статистику в разрезе дней чтобы отслеживать отклонения трафика.

Более подробно о написании запросов написал в статье « Получение рекламных кампаний Яндекс Директ с помощью API в DataFrame (Python)».

В данной статье я в большей степени расскажу о том, как структурировать данные и запросы, чтобы этим можно было нормально пользоваться.

Нам следует прописать запрос к серверу как функцию.

Лично я сделал 2 файла: функция с запросом и файл с данными, которые будут передаваться в функцию.

В первом файле пишем функцию


Я запрашиваю по всем проектам одни и те же поля, поэтому мне нужно передавать в запрос только даты, логин и токен.

Передача данных в функцию у меня выглядит следующим образом:

def rep(token,login,date_from,date_to):

Пишем запрос к серверу API Яндекс Директ


Данный запрос запрашивает данные по следующим параметрам:

  • Date
  • Impressions
  • Clicks
  • Ctr
  • Cost
  • AvgCpc
  • AvgImpressionPosition
  • AvgClickPosition
  • AvgTrafficVolume
  • BounceRate
  • AvgPageviews

Конечный файл с запросом


Код
import requests
from requests.exceptions import ConnectionError
from time import sleep
import json

# Метод для корректной обработки строк в кодировке UTF-8 как в Python 3, так и в Python 2
import sys

def rep(token,login,date_from,date_to):
    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 = token

    # Логин клиента рекламного агентства
    # Обязательный параметр, если запросы выполняются от имени рекламного агентства
    clientLogin = login

    # --- Подготовка запроса ---
    # Создание 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": {
                "DateFrom": date_from,
                "DateTo": date_to
            },
            "FieldNames": [
                "Date",
                "Impressions",
                "Clicks",
                "Ctr",
                "Cost",
                "AvgCpc",
                "AvgImpressionPosition",
                "AvgClickPosition",
                "AvgTrafficVolume",
                "BounceRate",
                "AvgPageviews",

            ],
            "ReportName": u("Report4"),
            "ReportType": "ACCOUNT_PERFORMANCE_REPORT",
            "DateRangeType": "CUSTOM_DATE",
            "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

    json_string = json.dumps(body)
    return req.text


2 Файл


Выносим даты, логины и токены отдельно как переменные.


Примерно так:

#токены
mytoken='blablablablaBLABLAsdfgsrgkdfgnf'

#логины
project = 'elama-99999999'

#Даты
DateFrom="2019-04-08"
DateTo="2019-04-16"

Это делается для того, чтобы легко менять информацию по всем клиентам, и даты отчетов.

Код для запроса статистики по проекту


print( ‘\n===Название проекта===')

data=rep(mytoken,project,DateFrom,DateTo)

file=open("cashe.csv","w")
file.write(data)
file.close()
f=DataFrame.from_csv("cashe.csv",header=1,sep='	',index_col=0,parse_dates=True)

f['Cost']=f['Cost']*1.2
f[‘Cost']=f['Cost']/1000000
f['AvgCpc']=f['AvgCpc']*1.2
f['AvgCpc']=f['AvgCpc']/1000000

print(f)

Подробнее:

  1. Название проекта ("=" используем для лучшего выделения, чтобы потом не потеряться в информации)
  2. Data — Записываем в эту строчку переменные, которые уже обозначили выше. (эта строчка и будет выполнять первый файл)
  3. Записываем ответ сервера в файл
  4. Открываем файл как DataFrame
  5. Добавляем к денежным значениям НДС.
  6. Переводим денежные значения в обычные рубли (стандартно API использует не рубли, а рубли*1000000.
  7. Выводим наш DataFrame



Второй файл выглядит следующим образом:


Код
#импорты
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
from НАЗВАНИЕ ФАЙЛА С ЗАПРОСОМ import rep

#Функции вывода Датафрейма
pd.set_option('display.max_columns',None)
pd.set_option('display.expand_frame_repr',False)
pd.set_option(‘max_colwidth',-1)

#токены
mytoken='blablablablaBLABLAsdfgsrgkdfgnf'

#логины
project = 'elama-99999999'

#Даты
DateFrom="2019-04-08"
DateTo="2019-04-16"

print( ‘\n===Название проекта===')

data=rep(mytoken,project,DateFrom,DateTo)

file=open("cashe.csv","w")
file.write(data)
file.close()
f=DataFrame.from_csv("cashe.csv",header=1,sep='	',index_col=0,parse_dates=True)

f['Cost']=f['Cost']*1.2
f[‘Cost']=f['Cost']/1000000
f['AvgCpc']=f['AvgCpc']*1.2
f['AvgCpc']=f['AvgCpc']/1000000

print(f)


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

После нам нужно будет лишь менять отрезок времени в полях DateFrom и DateTo.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 7

    0
    А не проще как-то сделать консольным это приложение и задавать даты в качестве параметров? Я уж не говорю про гуи, где еще проще указывать даты (Но гуи не проще писать!). :)
      0
      Сейчас не вижу в этом особого смысла: у меня всегда открыта среда разработки и мне для запуска требуется лишь заменить одну-две цифры и нажать запуск, это по времени не дольше выбора даты в интерфейсах)
      0
      А мы уже давно для 3000+ клиентов несколько раз в день и из директа, гугла, критео, матаргета, фб, вк и пр. перегружаем в google bigquery и, что главное, обновляем данные за предыдущие периоды, если рекламные системы вычищают из статы кликфрод через какое то время.

      Данные в разрезе дней, девайсов, объявлений, ключевиков и пр. Делаем обобщенное VIEW в BQ и выводим к себе в систему или Google DataStudio, Tableu и пр. Ну а маркетологи, контекстники и аналитики уже в bigquery sql запросы пишут если очень надо или если нужна какая-то сильно глубокая аналитика с заковыристыми sql-запросами.
        0
        Так и должно быть в идеале, но я еще нахожусь на уровне, где маркетолог сам себе аналитик. Сам понемногу иду к хранению информации в BigQuery.
        +1

        Я на R для этих же целей написал пакет ryandexdirect. При его использовании код намного компактнее т.к. пользователь уже готовые функции использует, ну и у меня токен автоматом сам обновляется при необходимости.


        Но в любом случае тоже решение интересное.

          0
          Ваш пакет уже почти стандарт рынка :) однако python дает больше возможностей в плане применения этой информации. Например я с помощью pandas отправляю всю статистику в BigQuery, чтобы потом сводить всю информацию в DataStudio
            0
            Python вообще язык широкого назначения, т.е. он даёт возможность придумать и написать какой нибудь веб сервис например, есть такое.
            А для отправки в BigQuery на R есть пакет Викхема bigrquery)

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое