<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0">

<channel>

<title>Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: Пятничный лонгрид</title>
<link>http://test.leftjoin.ru/tags/pyatnichny-longrid/</link>
<description></description>
<generator>E2 (v3365; Aegea)</generator>

<item>
<title>Собираем данные по рекламным кампаниям ВКонтакте</title>
<guid isPermaLink="false">41</guid>
<link>http://test.leftjoin.ru/all/get-data-from-vk/</link>
<comments>http://test.leftjoin.ru/all/get-data-from-vk/</comments>
<description>
&lt;p&gt;В пятничном лонгриде проделаем большую работу: возьмём информацию по рекламным кампаниям ВКонтакте и сопоставим их с данными Google Analytics в Redash. Чтобы снова не поднимать сервер, будем передавать данные через Google Docs, используя Spreadsheet API.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Получение access token&lt;/b&gt;&lt;br /&gt;
Для получение пользовательского ключа ВКонтакте нужно создать приложение. Идём в раздел «Разработчики» по &lt;a href="https://vk.com/apps?act=manage,"&gt;https://vk.com/apps?act=manage,&lt;/a&gt; жмём на кнопку «Создать приложение». В поле «Тип приложения» выбираем «Standalone-приложение» и даём любое название. После этого в меню слева идём в настройки и сохраняем себе ID приложения.&lt;/p&gt;
&lt;p class="note"&gt;Актуальную информацию о ключах можно посмотреть в статье &lt;a href="https://vk.com/dev/access_token" class="nu"&gt;«&lt;u&gt;Получение ключа доступа&lt;/u&gt;»&lt;/a&gt;&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/1-3.png" width="742" height="305" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Теперь копируем себе эту ссылку:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;https://oauth.vk.com/authorize?client_id=YourClientID&amp;amp;scope=ads&amp;amp;response_type=token&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Но вместо &lt;span class="inline-code"&gt;YourClientID&lt;/span&gt; вставляем ID своего созданного приложения. В scope у этой ссылки только ads, так что с этим ключом можно будет получать только информацию о рекламном кабинете.  Вставляем её в браузер и нас скидывает на другую страницу — в адресе этой странице будет указан ваш сгенерированный access token.&lt;/p&gt;
&lt;p class="note"&gt;Срок жизни токена — 86400 секунд: ровно сутки. Чтобы получить токен без временных ограничений можно добавить в scope параметр offline. Если токен понадобилось отозвать — смените пароль от страницы или в настройках безопасности завершите активные сессии.&lt;/p&gt;
&lt;p&gt;Ещё для запросов к API нам пригодится ID рекламного кабинета — проходим по &lt;a href="https://vk.com/ads?act=settings"&gt;https://vk.com/ads?act=settings&lt;/a&gt; и копируем «номер кабинета».&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Сбор данных через запросы к API&lt;/b&gt;&lt;br /&gt;
Напишем скрипт, который обращается к серверу ВКонтакте с нашим access token и номером рекламного кабинета и берёт информацию о всех кампаниях пользователя: количество просмотров на рекламах, кликов и затрат. Затем скрипт будет формировать из него DataFrame и отправлять в Google Docs.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;from oauth2client.service_account import ServiceAccountCredentials
from pandas import DataFrame
import requests
import gspread
import time&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Зададим несколько константных значений: access token, ID рекламного кабинета и версию API ВКонтакте, которую будем использовать. Актуальной является версия 5.103.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;token = 'fa258683fd418fafcab1fb1d41da4ec6cc62f60e152a63140c130a730829b1e0bc'
version = 5.103
id_rk = 123456789&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;За получение статистики по рекламе отвечает метод &lt;span class="inline-code"&gt;ads.getStatistics&lt;/span&gt;, но один из обязательных параметров при его вызове — ’ids’, ID рекламного объявления, статистику по которому мы хотим получить. Так как ID у нас пока нет, придётся сначала воспользоваться методов &lt;span class="inline-code"&gt;ads.getAds&lt;/span&gt;, который возвращает ID объявлений и кампаний.&lt;/p&gt;
&lt;p class="note"&gt;Подробнее со всеми методами ВКонтакте API можно ознакомиться в &lt;a href="https://vk.com/dev/methods"&gt;документации&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Библиотекой &lt;span class="inline-code"&gt;requests&lt;/span&gt; отправляем запрос к серверу и передаём свои параметры. Полученный ответ сразу переведём в формат json&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="Python"&gt;
campaign_ids = []
ads_ids = []
r = requests.get('https://api.vk.com/method/ads.getAds', params={
    'access_token': token,
    'v': version,
    'account_id': id_rk
})
data = r.json()['response']
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Вот, как выглядит объект &lt;span class="inline-code"&gt;data&lt;/span&gt;: нам вернулся обычный список словарей, с которым мы уже имели дело в материале &lt;a href="http://test.leftjoin.ru/all/give-json-data-to-redash/"&gt;“Передаём и анализируем собранные данные по рекламным капманиям в Redash”&lt;/a&gt;.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/2-2.png" width="993" height="328" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Заполняем словарь &lt;span class="inline-code"&gt;ad_campaign_dict&lt;/span&gt;. Ключом будет ID объявления, а значением — ID кампании, к которой принадлежит объявление. Так будет удобнее присваивать к объявлению ID кампании, к которой оно принадлежало.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;ad_campaign_dict = {}
for i in range(len(data)):
    ad_campaign_dict[data[i]['id']] = data[i]['campaign_id']&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь, имея ID каждого нужного объявления, можно обратиться к методу &lt;span class="inline-code"&gt;ads.getStatistics&lt;/span&gt;. Мы будем собирать количество просмотров, кликов, затрат и даты начала и конца объявления, поэтому заблаговременно заведём пустые списки.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;ads_campaign_list = []
ads_id_list = []
ads_impressions_list = []
ads_clicks_list = []
ads_spent_list = []
ads_day_start_list = []
ads_day_end_list = []&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Вызывать &lt;span class="inline-code"&gt;getStatistics&lt;/span&gt; нужно отдельно для каждого объявления — будем делать это в итераторе по &lt;span class="inline-code"&gt;ad_campaign_dict&lt;/span&gt;. Отправляем запрос, передавая в &lt;span class="inline-code"&gt;‘period’&lt;/span&gt; значение &lt;span class="inline-code"&gt;‘overall’&lt;/span&gt; — берём данные за всё время. У некоторых объявлений могут отсутствовать данные по полю «Просмотры» или «Клики» если они не были запущены, и, потребовав их, мы словим &lt;span class="inline-code"&gt;KeyError&lt;/span&gt; — во избежание этого добавим обработчик &lt;span class="inline-code"&gt;try — except&lt;/span&gt;, который заставит скрипт не обращать внимания на эту ошибку.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;for ad_id in ad_campaign_dict:
        r = requests.get('https://api.vk.com/method/ads.getStatistics', params={
            'access_token': token,
            'v': version,
            'account_id': id_rk,
            'ids_type': 'ad',
            'ids': ad_id,
            'period': 'overall',
            'date_from': '0',
            'date_to': '0'
        })
        try:
            data_stats = r.json()['response']
            for i in range(len(data_stats)):
                for j in range(len(data_stats[i]['stats'])):
                    ads_impressions_list.append(data_stats[i]['stats'][j]['impressions'])
                    ads_clicks_list.append(data_stats[i]['stats'][j]['clicks'])
                    ads_spent_list.append(data_stats[i]['stats'][j]['spent'])
                    ads_day_start_list.append(data_stats[i]['stats'][j]['day_from'])
                    ads_day_end_list.append(data_stats[i]['stats'][j]['day_to'])
                    ads_id_list.append(data_stats[i]['id'])
                    ads_campaign_list.append(ad_campaign_dict[ad_id])
        except KeyError:
            continue&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь сформируем из списков DataFrame и выведем первые 5 элементов:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;df = DataFrame()
df['campaign_id'] = ads_campaign_list
df['ad_id'] = ads_id_list
df['impressions'] = ads_impressions_list
df['clicks'] = ads_clicks_list
df['spent'] = ads_spent_list
df['day_start'] = ads_day_start_list
df['day_end'] = ads_day_end_list
print(df.head())&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/3-3.png" width="514" height="172" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;b&gt;Экспорт данных в Google Docs&lt;/b&gt;&lt;br /&gt;
Для экспорта DataFrame в таблицу Google Sheets необходим ключ доступа Google API. Пройдём по &lt;a href="https://console.developers.google.com"&gt;https://console.developers.google.com&lt;/a&gt; и создадим новый проект. Даём ему любое имя и в Dashboard жмём на кнопку “Подключить API и сервисы”. Нужно включить два API — Google Drive API и Google Sheets API. Ищем первый в поиске, нажимаем на “Включить API”, затем ищем второй и проделываем то же самое.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/4-2.png" width="771" height="233" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;После включения нас отправят на панель управления API. Жмём на «Создать учётные данные» — по ним будем проводить авторизацию в скрипте. Отмечаем, что используем Google Sheets API из веб-сервера и обращаемся к данным пользователя. Нажимаем на «Выбрать тип учётных данных» и создаем сервисный аккаунт. В поле «Роль» выбираем Проект — Редактор, а тип ключа оставим JSON.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/6-2.png" width="657" height="346" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;После этого нам отправят файл в формате JSON с нашими учетными данными — назовём его &lt;span class="inline-code"&gt;«credentials.json»&lt;/span&gt; — и перенаправят на страницу с сервисными аккаунтами. Ниже будет поле с почтой — копируем её себе.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/7-1.png" width="488" height="143" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Переходим по &lt;a href="https://docs.google.com/spreadsheets"&gt;https://docs.google.com/spreadsheets&lt;/a&gt; и создаем пустой файл с названием &lt;span class="inline-code"&gt;data&lt;/span&gt;, в который будут отправляться данные из DataFrame. В настройках доступа даём доступ по почте, скопированной ранее из сервисных аккаунтов — от неё будут приходить данные из скрипта.&lt;/p&gt;
&lt;p&gt;Закинем файл &lt;span class="inline-code"&gt;credentials.json&lt;/span&gt; в директорию со скриптом и продолжим писать код. Перечисляем область видимости в виде ссылок:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И при помощи библиотек &lt;span class="inline-code"&gt;oauth2client&lt;/span&gt; и &lt;span class="inline-code"&gt;gspread&lt;/span&gt; проводим авторизацию методами &lt;span class="inline-code"&gt;ServiceAccountCredentials.from_json_keyfile_name&lt;/span&gt; и &lt;span class="inline-code"&gt;gspread.authorize&lt;/span&gt;, указывая в параметрах первого наш файл и переменную scope. Через переменную &lt;span class="inline-code"&gt;sheet&lt;/span&gt; будем обращаться к нашему файлу в Google Docs.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;creds = ServiceAccountCredentials.from_json_keyfile_name('credentials.json', scope)
client = gspread.authorize(creds)
sheet = client.open('data').sheet1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для ввода значений в ячейку таблички есть метод &lt;span class="inline-code"&gt;update_cell&lt;/span&gt;. Важно: нумерация индексов ячеек при обращении начинается не с нуля, а с единицы. Первым циклом пройдём по первой строке и перенесем туда заголовки нашего DataFrame. Во втором будем идти по каждой ячейке и вставлять соответствующие значения DataFrame. По умолчанию стоит ограничение — 100 запросов в 100 секунд. Это ограничение может остановить наш скрипт на полпути: чтобы избежать ошибки пропишем &lt;span class="inline-code"&gt;time.sleep&lt;/span&gt;, чтобы после каждой вставки скрипт секунду выжидал.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;count_of_rows = len(df)
count_of_columns = len(df.columns)
for i in range(count_of_columns):
    sheet.update_cell(1, i + 1, list(df.columns)[i])
for i in range(1, count_of_rows + 1):
    for j in range(count_of_columns):
        sheet.update_cell(i + 1, j + 1, str(df.iloc[i, j]))
        time.sleep(1)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Если всё сделаем правильно — получим таблицу такого вида:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/8-1.png" width="831" height="377" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;b&gt;Экспорт данных в Redash&lt;/b&gt;&lt;/p&gt;
&lt;p class="note"&gt;Подключение Google Analytics к Redash описано в статье &lt;a href="http://test.leftjoin.ru/all/kak-podklyuchit-google-analytics-k-redash/" class="nu"&gt;«&lt;u&gt;Как подключить Google Analytics как Redash?&lt;/u&gt;»&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Имея в Redash таблицу с Google Analytics и рекламным кампаниям ВКонтакте, можем сопоставить их друг другу. Напишем такой запрос:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;SELECT
    query_50.day_start,
    CASE WHEN ga_source LIKE '%vk%' THEN 'vk.com' END AS source,
    query_50.spent,
    query_50.impressions,
    query_50.clicks,
    SUM(query_49.ga_sessions) AS sessions,
    SUM(query_49.ga_newUsers) AS users
FROM query_49
JOIN query_50
ON query_49.ga_date = query_50.day_start
WHERE query_49.ga_source LIKE '%vk%' AND DATE(query_49.ga_date) BETWEEN '2020-05-16' AND '2020-05-20'
GROUP BY query_49.ga_date, source&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;span class="inline-code"&gt;ga_source&lt;/span&gt; — источник, с которого человек пришел на сайт. Всё, что похоже на vk оператором &lt;span class="inline-code"&gt;CASE&lt;/span&gt; объединяем в столбец «vk.com». Оператором &lt;span class="inline-code"&gt;JOIN&lt;/span&gt; добавляем таблицу с данными из ВКонтакте, объединяя по полю даты. Отсеиваем данные — возьмём день последней рекламной кампании и посмотрим на несколько дней после него. На выходе получим таблицу такого вида:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/9-2.png" width="945" height="108" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;b&gt;Итоги&lt;/b&gt;&lt;br /&gt;
Получилась таблица, сообщающая, сколько всего было затрачено на объявления в этот день, сколько человек его посмотрели, зашли к нам на сайт и стали нашими новыми пользователями.&lt;/p&gt;
</description>
<pubDate>Fri, 22 May 2020 14:55:01 +0300</pubDate>
</item>


</channel>
</rss>