{"id":14268,"url":"\/distributions\/14268\/click?bit=1&hash=1e3309842e8b07895e75261917827295839cd5d4d57d48f0ca524f3f535a7946","title":"\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0442\u044c \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0430\u043c \u0438\u0433\u0440\u0430\u0442\u044c \u043d\u0430 \u0440\u0430\u0431\u043e\u0447\u0435\u043c \u043c\u0435\u0441\u0442\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e?","buttonText":"\u0423\u0437\u043d\u0430\u0442\u044c","imageUuid":"f71e1caf-7964-5525-98be-104bb436cb54"}

Python для создания идеального поста во «ВКонтакте»

Несколько месяцев назад работал над проектом, одной из главных задач которого был максимальный охват постов во «ВКонтакте». Для выведения формулы идеального поста написал скрипт на Python, который парсил записи группы и группировал данные по часу, количеству символов и типу поста.

Сразу скажу, скрипт написан далеко не идеально, я маркетолог, а не программист.

Пример работы

Для примера работы скрипта взял группу Life News. На момент написания статьи было собрано 92 207 постов, последний пост датирован 1 апреля 2017 года. 17 минут затрачено на скачивание, и 5 секунд — на обработку данных.

Выводы:

  • Лучшее время для размещения записей — с 19:00 до 22:00. Этот интервал показал максимальные количество просмотров сумму активностей на пост. Интересно, что больше всего комментариев зафиксировано в дневное время, с 11:00 до 14:00, но количество комментариев на пост больше в вечернее время, с 20:00 до 22:00.
  • 91% записей в сообществе короткие, до 200 символов.
  • Материалы с видео набирают в два раза больше активностей, но при этом среднее количество просмотров на пост одинаковое.
  • Посты без встроенной ссылки собирают больше активностей на пост, и ER в два раза выше.

Создание токена

Для работы с API «ВКонтакта» необходимо сгенерировать сервисный токен. На эту тему написано много инструкций, поэтому буду краток.

1. Перейдите на страницу создания приложения и создайте приложение «Standalone-приложение»:

2. После создания приложения перейдите в настройки и сохраните сервисный ключ доступа.

Установка дистрибутива Anaconda

Anaconda — это дистрибутив для работы с Python, в него входит много библиотек и инструментов. Нам для реализации данного скрипта понадобится Jupiter Notebook и библиотека Pandas.

  1. Запустите установщик и просто следуйте инструкциям.
  2. После успешной установки перейдите в папку, где будет располагаться ваш проект, и в строке адреса введите CMD и нажмите Enter.
  3. У вас откроется командная строка, введите в неё команду Jupyter Notebook и нажмите Enter.
  4. Откроется страница Jupyter Notebook в браузере, после чего создайте новый файл Python 3.

Работа со скриптом

Собирать данные можно как из своих групп, так и групп конкурентов. Количество постов для анализа устанавливается вручную. Собираются все стандартные данные: количество символов в тексте, сам текст, количество просмотров, комментариев, лайков и вложений.

Ввод исходных данных и подключение библиотек

import requests import pandas as pd from datetime import datetime import time id_group = 'id_group' token = 'token' count_posts = 10000

Первый этап разделен на два промежуточных. В первом мы импортируем необходимые для работы библиотеки. Request — для получения данных из «ВКонтакте», Pandas — для работы с данными, DateTime — для работы с датами, библиотека Time — для создания интервалов между запросами.

На втором этапе необходимо ввести исходные данные. В поле id_group — домен группы, token — токен, полученный на предыдущем этапе, count_posts — количество постов для анализа. Для того чтобы спарсить все записи, просто введите 1 000 000, алгоритм сам возьмёт максимальное количество постов.

После ввода кода в Jupiter Notebook нажимаем Ctrl+Enter и переходим на следующую строку для ввода второй части кода.

Подключение к API и загрузка данных

Для подключения к API мы использовали метод wall.get, более подробное описание методов API в официальной справке.

Вторая часть кода с помощью get-запроса инициирует обращение к API. Не буду подробно останавливаться на get-запросе, просто скажу, что он состоит из двух параметров: URL — ссылка для запроса, и Params — параметр запроса. В словаре params, в зависимости от ваших нужд, можно поменять переменную filter на owner — посты только от владельца, all — все посты, others — гостевые посты.

Подробное описание всех параметров доступно в справке.

Ответ получаем в JSON-формате и записываем его в переменную r. Из-за ограничений API за одно обращение мы можем забрать только 100 постов. Поэтому за каждую итерацию цикла 100 постов добавляются в массив данных data_posts. Также для обхода ограничения по количеству запросов в секунду я добавил time.sleep(0.5), что позволяет сделать паузу в полсекунды между запросами.

Обработка и запись данных в Data Frame

Для работы с данными нам понадобится библиотека Pandas. Pandas — одна из самых популярных библиотек для анализа и обработки данных. Работа с библиотекой строится через объект Data Frame, внешне он напоминает таблицу, но это не совсем так. Для начала работы с DF нам необходимо преобразовать и обработать данные из JSON-формата.

stats = [] for record in data_posts: title = record['text'].split('\n')[0] if len(title) > 80: title = title[:80] len_title = len(record['text']) len_title = len_title // 100 *100 date = datetime.fromtimestamp(record['date']).strftime('%Y-%m-%d') hour = datetime.fromtimestamp(record['date']).strftime('%H') attachment = {'photo' :0, 'audio' :0, 'video': 0 , 'link': 0, 'poll': 0} if 'attachments' in record: for attach in record['attachments']: if attach['type'] in attachment: attachment[attach['type']] = attachment[attach['type']] + 1 if 'views' in record: views = record['views']['count'] else: views = 0

Для начала создадим пустой массив, где будут храниться данные stats = []. Как вы помните, мы записывали все данные поступающие из «ВКонтакте» в массив data_posts. Теперь наша задача вытащить из него только нужное. Для обхода всего массива мы создаём цикл For, такая конструкция for record in data_posts: позволит работать с одним постом за один обход цикла.

За название поста я брал первую строчку и ограничил длину 80 символами. Это подходило исключительно для моего проекта, вы переделайте алгоритм под ваш проект. Для того чтобы записать в переменную title текст, достаточно title = record['text']. Далее я замерил длину текста и разделил на 100, взяв только целое число для удобной группировки.

Переменные Date и Hour — это дата в формате «гггг-мм-дд» и час выхода поста соответственно. Следующий блок кода — это поиск и подсчёт количества вложений. Все они перечислены в словаре attachments.

Последнее условие проверяет, есть ли в данных о посте количество просмотров. «ВКонтакте» относительно недавно ввела количество просмотров в записи, и поэтому при получении постов, опубликованных раньше 2017 года, скрипт выдавал ошибку, так как ключ Views отсутствовал. По этой причине я записывал в такие посты 0;

total_actions = record['comments']['count'] + record['likes']['count'] + record['reposts']['count'] stats.append([title, len_title, attachment['photo'], attachment['audio'], attachment['video'], attachment['link'], attachment['poll'], views , record['comments']['count'], record['likes']['count'], record['reposts']['count'], total_actions, date, hour])

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

columns = ["name_post", 'len_text', 'photo', 'audio', 'video', 'link', 'poll', "views", "comments", "likes", "share", 'total_action', "date", "hour"] df = pd.DataFrame(data=stats, columns=columns)

В последнем фрагменте третьей части мы записываем наши данные в Data Frame. Сначала необходимо задать название столбцов, я задал их через массив columns. После чего мы создаём объект Data Frame и в качестве аргументов передаём в него массив данных stats и название заголовков columns. Готово, объект создан, и теперь с ним можно работать.

Вычисление показателей и запись в Excel

Получившийся Data Frame группируем по часу, типу постов и длине текстов и вычисляем показатели вроде средней активности на пост, просмотров на пост и ER.

df_hour = df.drop(['len_text', 'photo', 'audio', 'video', 'link', 'poll'], axis=1) df_group_by_hour = df_hour.groupby('hour').sum() df_group_by_hour['count_post'] = df_hour.groupby('hour')['name_post'].count() df_group_by_hour['mean_action'] = df_group_by_hour['total_action'] /df_group_by_hour['count_post'] df_group_by_hour['views_on_post'] = df_group_by_hour['views'] / df_group_by_hour['count_post'] df_group_by_hour['er'] = df_group_by_hour['total_action'] / df_group_by_hour['views'] * 100 df_group_by_hour = df_group_by_hour.sort_values(by="er", ascending=False) df_type = df.drop(['date', 'hour'], axis=1) df_group_by_len_title = df_type.groupby('len_text').sum() df_group_by_len_title['count_posts'] = df_type.groupby('len_text')['name_post'].count() df_group_by_len_title['mean_action'] = df_group_by_len_title['total_action'] / df_group_by_len_title['count_posts'] df_group_by_len_title['views_on_post'] = df_group_by_len_title['views'] / df_group_by_len_title['count_posts'] df_group_by_len_title['er'] = df_group_by_len_title['total_action'] / df_group_by_len_title['views'] * 100 df_group_by_len_title = df_group_by_len_title.sort_values(by='views', ascending=False) df_group_by_len_title = df_group_by_len_title.style.format("{:.2f}")

И записываем в Excel-файл.

with pd.ExcelWriter('data_vk_{}.xlsx'.format(id_group)) as writer: df.to_excel(writer, index = False , sheet_name='Исходный DataFrame') df_group_by_hour.to_excel(writer, index = True, sheet_name='Группировка по часу') df_group_by_len_title.to_excel(writer, index = True, sheet_name='Группировка по кол-ву символов') for atach in ['photo','audio', 'video','link','poll']: df_group_by_temp = df_type.groupby(atach).sum() df_group_by_temp = df_group_by_temp.loc[:,["views", "comments", "likes", "share", 'total_action']] df_group_by_temp['count_posts'] = df_type.groupby(atach)['name_post'].count() df_group_by_temp['mean_action'] = df_group_by_temp['total_action'] / df_group_by_temp['count_posts'] df_group_by_temp['views_on_post'] = df_group_by_temp['views'] / df_group_by_temp['count_posts'] df_group_by_temp['er'] = df_group_by_temp['total_action'] / df_group_by_temp['views'] * 100 df_group_by_temp = df_group_by_temp.sort_values(by='er', ascending=False) df_group_by_temp = df_group_by_temp.style.format("{:.2f}") sheet_name = 'Группировка по ' + atach df_group_by_temp.to_excel(writer, index = True, sheet_name=sheet_name)

Файл записывается туда же, где находится файл Jypter Notebook. Он содержит исходные необработанные данные, сгруппированные данные по часу, сгруппированные данные по количеству символов и группировку по вложениям.

Надеюсь, данный гайд поможет вам найти формулу вашего идеального поста.

0
20 комментариев
Написать комментарий...
Семен Смирнов
я маркетолог, а не программист

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

Все полученные факты будут верны тольк для группы Life News, но никак не помогут в других местах. Но и это не все

Лучшее время для постинга с 19:00 до 22:00

В любой группе, если выгрузить данные, будет интервал, где больше лайков и активности. Это не лучше время для постинга, а просто факт. Корни его растут из среднего возраста аудитории, их времяпровождения, и коррелирует с тематикой конкретного поста, общей тематикой постов сообщества.

Не учитывать это нельзя, нельзя только на время смотреть

Посты без встроенной ссылки собирают больше активностей на пост и ER в 2 раза выше.

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

И потом в статье все прямо красиво про обработку, душа радуется. Надеюсь, вам не будет обидно из-за написанного выше

Ответить
Развернуть ветку
Марков Вячеслав
Автор

Семён, спасибо за обратную связь. Насколько я понял, вы подумали, что я выводы написал про весь ВК, но это не так. Я показал как можно работать с каждым конкретным сообществом и все выводы делать под свои группы. А что по поводу ER действительно слабый вывод, стыдно за него)

Ответить
Развернуть ветку
Семен Смирнов

Да, вообще так и подумал

Для анализа одного конкретного сообщества там все по делу, парсинг красивый

Но аналитика в любом случае потом нужна продвинутая для правильных выводов. Тема для отдельной статьи

Ответить
Развернуть ветку
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
Сергей Щетинин

Давайте просто доделаем, запилим в онлайн сервис и будем продавать?

Ответить
Развернуть ветку
Марков Вячеслав
Автор

Уже делаю) Но пока проблемы с клиент серверной архитектурой и отлаженным выполнением задач.

Ответить
Развернуть ветку
Astemir Boziy

Есть ссылка на гитхаб?

Ответить
Развернуть ветку
Владислав Бакаев

Могу хелпануть

Ответить
Развернуть ветку
Roman Kuvshinnikov

Прикольный кейс) Вот ещё инструкция как работать с API VK через сервис RockStat: https://digitalgod.be/guides/vk_ads_api

Ответить
Развернуть ветку
Марков Вячеслав
Автор

С удовольствием читал данный кейс. Более того воплотил его в жизнь на их платформе. Одно огорчает, что новые статьи пока не добавляются.

Ответить
Развернуть ветку
Roman Kuvshinnikov
Ответить
Развернуть ветку
Крымский Чайничек

А не пробовал просто подойти по другому к постам? Ну разбить их на тех кто с картинками, кто с видео?

Ответить
Развернуть ветку
Владимир

Тот случай, когда Jupyter используется не для хипстерского ML, а чтобы написать парсер (:

Ответить
Развернуть ветку
Denis Veselov

Залейте на google colab свой ноутбук и не мучайте людей установкой анаконды )

Ответить
Развернуть ветку
Дарья Ронжина

Какой приятный туториал! Вообще отличное решение, например, когда вы работаете в компании с жестким nda. Конечно, подключить амплифер и тд. куда проще и удобнее, но когда нет ограничений. Поэтому полезно, попробую

Ответить
Развернуть ветку
Reki Lubvi

Спасибо за статью, очень интересно.
Можно для тех, кто в душе блондинка, подробнее описать момент создания приложения и получения данных? выходит ошибка про неверный параметр request - видимо приложение не работает как надо.

Ответить
Развернуть ветку
Марков Вячеслав
Автор

Скиньте ссылку на ваш notebook

Ответить
Развернуть ветку
Александр Соколов

После второй строки выдает KeyError: 'response' и останавливается. Что делать?

Ответить
Развернуть ветку
Kirill Malev

Вячеслав, удалите из ноутбука свой ключик

Ответить
Развернуть ветку
Марков Вячеслав
Автор

Это сервисный ключ, а не авторизационный, он доступа к личным данным не даёт. 

Ответить
Развернуть ветку
17 комментариев
Раскрывать всегда