(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Мониторинг активности групп VK. Обрабатываем данные на VKScript

Источник: habrahabr
isxam

Столкнулся с задачей мониторинга активности пользователей всем известной социальной сети. Передо мной стояла задача собирать данные о количестве пользователей, находящихся онлайн в определенной группе или сообществе.

Инструменты

Поскольку сам я занимаюсь веб-разработкой, то инструменты мной использовались такие

  • PHP 5 (Zend Framework)
  • Vk API
  • Cron

Объясню свой выбор - Vk API - дело в том, что получить количество пользователей онлайн можно и без API, а спарсив страницу поиска по пользователям с фильтром сообщества и метки онлайн, однако я предпочел не возится с авторизацией и разбором тегов, а применить программный интерфейс.

Архитектура

Реализацию условно можно разделить на 2 части. Первая - скрипт, который на id группы находит количество пользователей онлайн и записывает его в БД. Вторая - админка, позволяющая добавлять новый группы для мониторинга и просматривать статистику по уже добавленным группам.
Что бы статистика была актуальна, необходимо как можно чаще мониторить состояние группы в текущий момент времени. Скрипт стоит повесить в Cron, пусть он у нас вызывается каждых 5 минут.

Обзор Vk API

Если с админкой все более-менее понятно, то вот со скриптом сбора статистики не совсем. Ознакомившись с методами, предоставляемыми API, прихожу к первому решению.

Первое решение (неверное)

С помощью методов groups.getMembersusers.get получаем список участников группы и их статус - онлайн или оффлайн. Далее считаем сколько пользователей онлайн. Все просто. Однако кажущаяся простота в результате приносит ряд проблем.
Все бы хорошо, если у Вас группы маленькой численностью (до 1000 человек). В противном случае упираемся в ограничения API - за один раз можно получить информацию только о 1000 пользователей. Что нам это ограничение - можно же вызывать метод в цикле, но нет. Производить вызовы API разрешено не чаще 3 запросов в секунду.
Посчитаем примерное количество запросов которое понадобится. Возьмем сообщество habrahabr Vk. Оно насчитывает более 40.000 пользователей, следовательно нам понадобится ~40 запросов чтобы получить членов сообщества и 40 запросов - их статус.
Отправляемся искать новое решение.

Второе решение (верное)

Обнаруживаем в документации метод execute 

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

Принимает он на вход строку с кодом написанным на так называемом VKScript (похож на javascript). Проблема лишь в том, что вменяемая документация по этому методу и самому языку отсутствует. Вероятно решение найдено, так что можно углубится в изучение API Vk и VKScript в частности.

Работа с API

Скачиваем класс для работы с API, предлагаемый разработчиками. Я привел его только к более приемлемому виду, чтобы он вписался в coding style применяемый в Zend Framework.

Класс Api
<?php
class Vkapi_Model_Api {

    private $_accessToken = null;
    
    private $_apiUrl = 'https://api.vk.com/method/';
    
    public function __construct($accessToken) {
        
        $this->_accessToken = $accessToken;
        
    }
    
    public function api($method, $params = array())
    {
        $params['access_token'] = $this->_accessToken;
        $query = $this->_apiUrl. $method . '?' . $this->_params($params);
        $responseStr = file_get_contents($query);
        if(!is_string($responseStr)){
            return null;
        }
        
        $responseObj = json_decode($responseStr);
        return $responseObj;	
    }
    
    private function _params($params) {
        $pice = array();
        foreach($params as $k=>$v) {
            $pice[] = $k.'='.urlencode($v);
        }
        return implode('&',$pice);
    }
}

Аутентификацию и авторизацию я описывать не буду, так как она осуществляется через OAuth, много информации в рунете, да и на странице Vk API.

Осуществим пробный вызов к API - получим первые 20 постов в группе habrahabr

    public function wallsAction()
    {
        //.......
        $api = new Vkapi_Model_Api($accessToken);
        
        $response = $api->api('wall.get',array('owner_id' => '-20629724'));
        $this->view->walls = $response->response;
    }

image

Сейчас сделаем тоже самое, только через метод execute

    public function wallsAction()
    {
        //.......
        $api = new Vkapi_Model_Api($accessToken);
        $code = "
        	var walls = API.wall.get({ owner_id : -20629724 });
        	return walls;
        ";
        $response = $api->api('execute',array('code' => $code ));
        $this->view->walls = $response->response;
    }

В итоге получаем один и тот же результат.
Одно что плохо - это то, что мы смешали код VKScript и PHP. Выглядит это очень плохо. Займемся рефакторингом.
Было бы неплохо, чтобы каждый скрипт хранился в отдельном файле и вызвать его можно было бы одной функцией. Еще необходимо предусмотреть то, что в последствии нам еще понадобится передавать какие-то данные в этот скрипт ( сейчас например  owner_id  жестко забит в код).

Выносим VKScript в отдельные файлы

В корне нашего модуля создадим папку с названием "vkscripts", в нее будем складывать наши скрипты (например getWalls.vks). Пропишем путь к скриптам в config-файле application.ini

vkapi.scripts.path = APPLICATION_PATH "/modules/vkapi/vkscripts"

Нам нужен класс, который был бы удобен для вызова скриптов, расположенных в этой директории. Воспользуемся возможностями PHP5, а именно магическим методом __call. По названию вызываемого метода мы будем искать скрипт с таким названием.

Исходник класса
<?php

class Vkapi_Model_Executor
{
    private $_api;
    
    public function __construct($api)
    {
        $this->_api = $api;
    }
    
    public function __call( $methodName, $arguments )
    {
        $script = $this->_getScript($methodName);
        if(count($arguments)){
            $script = $this->_prepareParams($script, $arguments[0]);
        }		
        $response = $this->_api->api('execute', array('code' => $script));
        if( $error = $this->_getError($response) ){
        	throw new Exception($error->error_msg, $error->error_code);
        }
        
        return $response->response;	
    }
    
    private function _getError($response)
    {
        if( isset($response->error) ){
            $error = $response->error;
            return $error;			
        }
        return null;	
    }
    
    
    private function _getScript( $name )
    {
        $scriptsPath = Zend_Registry::get('vkapi_config')->scripts->path;
        
        $filePath = $scriptsPath . '/' . $name . '.vks';
        if(is_file($filePath)){
            $script = file_get_contents($filePath);
            return $script;
        }
        
        return null;
    }
}

Итак, давайте что-нибудь сделаем с этим классом.
В папку vkscripts кладем файл  getWalls.vks  с таким содержимым

var walls = API.wall.get({ owner_id : -20629724 });
return walls;

В контроллере:

    public function wallsAction()
    {
        //.......
        $api = new Vkapi_Model_Api($accessToken);
        $executor = new Vkapi_Model_Executor($api);
        $response = $executor->getWalls();
        $this->view->walls = $response->response;
    }

Мы получили тот же результат, только налицо существенные плюсы: мы разнесли код в отдельные файлы, сделали его более читабельным, упростили вызов execute.
Следующий шаг - добавление возможности передавать параметры в наш скрипт. Воспользуемся для этого неким представлением. В коде VKScript вначале при необходимости что-то получить на вход будем писать так:

var groupId = %GROUP_ID%;
var offset = %OFFSET%;

// .... здесь пошел наш код

А в нашем классе будем перед вызовом api с этим кодом заменять %VAR_NAME% на значение переменной.
Допишем наш класс Executor следующим образом

Исходник доработанного класса
<source lang="php">
<?php

class Vkapi_Model_Executor
{
    // ......
    
    public function __call( $methodName, $arguments )
    {
        $script = $this->_getScript($methodName);
        if(count($arguments)){
            $script = $this->_prepareParams($script, $arguments[0]);
        }		
        $response = $this->_api->api('execute', array('code' => $script));
        if( $error = $this->_getError($response) ){
        	throw new Exception($error->error_msg, $error->error_code);
        }
        
        return $response->response;	
    }

    // ......
    
    private function _prepareParams($script, $params)
    {
        
        foreach ($params as $key => $value){
            $script = str_replace('%' . strtoupper($key) . '%', $value, $script);
        }
        
        return $script;	
    
    }

}

В контроллере же при необходимости передачи параметром пишем следующее

    public function wallsAction()
    {
        //.......
        $api = new Vkapi_Model_Api($accessToken);
        $executor = new Vkapi_Model_Executor($api);
        $response = $executor->getWalls(array(
            'group_id'	=> -20629724,
            'offset'	=> 0
        ));
        $this->view->walls = $response->response;
    }

Что соответственно подставит в наш скрипт вместо %GROUP_ID% и %OFFSET% переданные значения.
Вот как выглядит структура модуля
image

Получаем количество пользователей онлайн

Cуществует ограничение на вызов методов API в execute. Лимит 22 вызова (найден практически). Так же в паутине я не нашел информации о том, что и на другие операторы (например сложение, вычитание ) тоже существуют ограничения, однако они есть. Поскольку если пробегать по массиву пользователей и считать количество онлайн я получал ошибку о превышенном числе операций, то было решено возвращать из execute полный список пользователей, после чего уже на стороне моего сервера считать их количество.
Из-за ограничения в числе запросов к API в методе execute нам все равно придется выполнить как минимум 1 запрос на 10.000 участников группы, потому что для обработки 1.000 требуется 2 запроса.
Вот скрипт который получился

var groupId = %GROUP_ID%;
var offset = %OFFSET%;

// API call limit
var _acl = 22;
var members = API.groups.getMembers({ gid : groupId }); _acl = _acl - 1;
var count = members.count;
var users = [];

while( _acl > 1 && offset < count){
    var _members = API.groups.getMembers({ gid : groupId, offset : offset }); _acl = _acl - 1;
    users = users + API.users.get({ uids : members.users, fields : "online" }); _acl = _acl - 1;
    offset = offset + 1000;
}
var result = {
    count	: count,
    offset	: offset,
    users	: users@.online
};
return result;

Немного прокомментирую свой код. Счетчик _acl - для предотвращения ошибки из-за превышения лимита операций с API. users@.online - возвращаем только список значений [0,1,1,0,0,0,1,0,1] онлайн-оффлайн. 
В контроллере вызываем этот скрипт, последовательно увеличивая offset, пока не пробежимся по всем участникам группы.

    $count = 1;
    $offset = 0;
    $nowOnline = 0;		
    while($count > $offset){
        $users = $executor->getOnline(array(
            'group_id'	=> $groupId,
            'offset'	=> $offset
        ));
        $count = $users->count;
        $offset = $users->offset;
        foreach ( $users->users as $online){
            if($online){
                $nowOnline++;
            }
        }
    }	

Итак протестим и увидим - данные полученные через API почти совпадают с данными с vk.com, возможно эта неточность из-за кешей, или по другой причине, не видной извне.

Замечания

VKScript не поддерживает функции, операторы инкремента, декремента. 

Итог

Мы разработали инструментарий для работы с API vk.com через метод execute. С помошью его можно разрабатывать приложения сбора статистики и т.д. причем выглядеть это будет очень даже приглядно. Прикрутить к этому всему интерфейс - это уже тривиальная задача. В конце замечу, что другая социальная сеть Facebook предоставляет доступ к исполнению кода, написанного на языке называемом FQL (Facebook Query Language, схож с SQL), у которого возможностей явно побольше чем у VKScript со всеми его ограничениями.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 16.11.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
IBM DOMINO ENTERPRISE CLIENT ACCESS LICENSE AUTHORIZED USER ANNUAL SW SUBSCRIPTION & SUPPORT RENEWAL
Купить Антивирус Dr.Web Server Security Suite для сервера
VMware Workstation 14 Pro for Linux and Windows, ESD
ABBYY Lingvo x6 Европейская Профессиональная версия, электронный ключ
Stimulsoft Reports Server Team 10 users
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
СУБД Oracle "с нуля"
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
Новые материалы
Вопросы и ответы по MS SQL Server
Corel DRAW - от идеи до реализации
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100