Получение и подсчет количества репостов VK.COM

от автора

На днях стояла задача посчитать количество пользователей сайта vk.com, поделившихся определенным постом (т.е. количество репостов). Для этого существует приложение «Вирусоанализатор», но в процессе использования выяснилось, что оно не считает большое количество репостов (от 100 и более). Ползунок доходит почти до конца и на этом останавливается. А т.к. уже был объявлен конкурс на наибольшее количество репостов на определенную запись, то нужна была альтернатива. Которой не оказалось…

Поэтому пришлось обращаться к API Вконтакте и искать как реализовать данную задачу. Сразу скажу, что не пришлось создавать standalone-приложения для этой цели. Все получилось реализовать при помощи методов, не требующих access_token. Ниже представлен список методов API, используемых в данной задаче:

  • likes.getList — получение списка пользователей, который нажали «Мне нравится» или «поделились» постом
  • users.get — получение информации о пользователях по их uid или коротким именам
  • wall.get — получение новостей со страницы пользователя.

Из плюсов данного расчета можно выделить только то, что не нужно создавать приложение для этой цели.
Из минусов (если изучить эти API методы): неудобный поиск нашего репоста у пользователя. Вконтакте разрешает получить методом wall.get максимум 100 новостей. Разумеется данный метод поддерживает параметр offset (смещение по новостям), но все же я ограничился определенным количеством новостей для обработки (500 штук).

Некоторые возникшие проблемы:

Итак, нам нужно было данные о тех пользователях и их постах, которые поделились нашим репостом. Для получения этого списка используется метод API likes.getList, у которого есть параметр filter, принимающий два возможных значения:

  • likes — пользователи, которые нажали «Мне нравится»
  • copies — пользователи, которые поделились постом. Именно это и нужно.

Но когда уже что-то было готово, выявилось, что данные, полученные при помощи API не сходятся с реальными.

Например, пользователь который «поделился» не доставался методом likes.getList?filter=copies, но находился в списке, которым просто «понравилась» запись. К тому попался еще один пользователей с ситуацией с точностью до наоборот.

Поэтому для более верных результатов пришло обрабатывать весь список (пользователи, нажавшие «Мне нравится» и «Поделиться»).

Ниже приведен класс, написанный на PHP для поиска количества репостов:

Показать код

class VKLIKES {	 		 	private $url = 'https://api.vk.com/method/'; // URL к методам API 		 	private $owner_id = '-33116400'; //ID автора поста 	private $post_id = '1597'; //ID поста 		 	private $count = 1000; //По сколько "репостов" и "лайков" доставать 	private $users = array(); //Массив с пользователями 	private $countReposts; //Количество репостов у текущего пользователя 	private $findPost; //ID найденного репоста в пользовательских новостях 	private $find; //Флаг найден/не найден репост у пользователя  	public function __construct($owner_id = '', $post_id = '') { 		$this->owner_id = $owner_id; 		$this->post_id = $post_id;			 	}  	//Сообщение о действиях 	private function printProgress($text, $start = true, $error = false) {			 		if ($error) $color = 'red'; 		else if ($start) $color = '#444'; 		else $color = 'green'; 			 		echo '<li style="color: '.$color.';">'.$text.'</li>'; 		ob_flush(); 		flush(); 	}  	/*Считываем всех пользователей, кто поделился нашим постом 	* $onwer_id - ID автора поста 	 * $post_id - ID-поста 	 * $fitler - "likes" или "copies" 	 * $offset - смещение по пользователям. Можно достать максимум 1000 пользователей 	* $onlyCount - вернуть только количество репостов 	 * $start - используется для рекурсии 	 */ 	private function getUsers($owner_id, $post_id, $filter, $offset = 0, $onlyCount = false, $start = true) {			 		//формируем URL со всеми параметрами 		$url = $this->url.'likes.getList?type=post&friends_only=0&offset='.$offset.'&count='.$this->count.'&owner_id='.$owner_id.'&item_id='.$post_id.'&filter='.$filter; 			 		//получаем результат запроса в JSON-фомате 		$json = file_get_contents($url);		 		//преобразуем JSON в ассоциативный массив 		$data = json_decode($json, true); 			 		//если ответ, не содержит нужных данных 		if (!isset($data['response'])) return false; 			 		$response = $data['response'];	 		$count = $response['count']; //Получаем количество пользователей 			 		//Понадобится, когда нужно будем определить ТОЛЬКО количество репостов у пользователя 		if ($onlyCount) { 			$this->countReposts = $count; 			return true; 		} 			 		//Далее рекурсивно будет получать пользователей до тех пор, пока не считаем все. При этом сдвигаем offset. 		$users = $response['users']; 			 		if (count($users) == 0) return false;  			 		if ($start) { 			$this->users = $users; 		} else { 			$this->users = array_merge($this->users, $users); 		} 			 		$offset += $this->count; 			 		$this->getUsers($owner_id, $post_id, $filter, $offset, $onlyCount, false);		 	}  	//Для удобства я изменил ключи в массиве. Ключами являются - ID пользователя сайта vk.com 	private function remakeUsersArray($usersWithInfo) { 		$new = array(); 		foreach ($usersWithInfo as $value) { 			$new[$value['uid']] = $value; 		} 			 		return $new; 	}  	/* Получить информацию о пользователях 	 * $vkIDs - массив с ID пользователей 	 */ 	function getUsersInfo($vkIDs) { 		//$count = 1000;  		//Для получения информации о пользователе, используются положительные ID (ID со знаком минус имеют группы, сообщества) 		foreach ($vkIDs as $key => $val) { 			if ((int)$val < 0) unset($vkIDs[$key]); 		} 						 		$uids = implode(',', $vkIDs); 		$fields = 'uid,first_name,last_name,nickname,screen_name,sex,city,country,timezone,photo,photo_medium,photo_big,has_mobile,rate,online,counters'; 		$url = $this->url.'users.get?&uids='.$uids.'&fields='.$fields.'&name_case=nom'; 			 		$json = file_get_contents($url); 		$data = json_decode($json, true); 		if (isset($data['response'])) { 			$response = $data['response'];			 			return $response; 		}  		return 0; 	}	  	//Получам посты пользователя 	private function getUsersPosts($owner_id, $offset = 0) { 		$maxNews = 600; //Максимальное колчиство новостей для поиска 		$count = 100; //100 - это максимальное количество новостей, которые можно получить за один запрос 			 		//Если обыскали $maxNews новостей и не нашли 		if ($offset > $maxNews - $count) { 			$this->printProgress('<b>Репост не был найден среди '.$maxNews.' новостей...</b>', false, true); 			$this->find = false; 			return false; 		}			 		//Формируем URL 		$url = $this->url.'wall.get?'; 		$url .= 'owner_id='.$owner_id.'&'; 		$url .= 'offset='.$offset.'&'; 		$url .= 'count='.$count.'&'; 		$url .= 'filter=owner'; 			 		$json = file_get_contents($url); //Получаем JSON-ответ 		$data = json_decode($json, true);  			 		//Если вдруг страница пользователя "заморожена" или удалена  		if (!isset($data['response'])) { 			$this->printProgress('<b>Ошибка получения нововстей</b>', false, true); 			$this->find = false; 			return false; 		} 			 		$response = $data['response']; 		$this->printProgress('Поиск нашего репоста среди '.($count + $offset).' новостей..'); 			 		//Обрабатываем $count новостей 		foreach ($response as $news) { 			if (!is_array($news)) continue;			 				 			/* copy_owner_id - ID моей страницы или группы 			 * copy_owner_id - ID моего поста 			 */				 			if (isset($news['copy_owner_id'], $news['copy_post_id']) and $news['copy_owner_id'] == $this->owner_id and $news['copy_post_id'] == $this->post_id) {					 				$this->users[$news['from_id']]['repost_id'] = $news['id']; 				$this->printProgress('<b>Репост успешно найден найден #'.$news['id'].'</b>', false); 				$this->findPost = $news['id']; 				$this->find = true; 				return true; 			}	 		} 						 		$offset += $count; //Увеличиваем смещение				 		$this->getUsersPosts($owner_id, $offset); //Рекурсия 	}  	//Поиск репоста 	function findReposts() {								 		echo '<ul>'; 		$this->printProgress('Получаю список ID пользователей, сделавших репост...');					 		$this->getUsers($this->owner_id, $this->post_id, 'copies');		 		$this->printProgress('Список ID успешно получен', false);	 		$copies = $this->users;			 					 		$this->printProgress('Получаю список ID пользователей, сделавших лайк...');			 		$this->getUsers($this->owner_id, $this->post_id, 'likes'); 		$this->printProgress('Список ID успешно получен', false);			 			 		foreach ($this->users as $id) { 			if (in_array($id, $copies)) continue; 			$copies[] = $id; 		} 				 			 		$this->users = $copies; 		$this->printProgress('<b>Уникальных ID пользователей для получения их информации: '.count($this->users).'</b>');	 			 		$this->printProgress('Получаю информацию о пользователях по их ID...');			 		$usersWithInfo = $this->getUsersInfo($this->users);		 				 		$this->printProgress('Информация была успешно получена', false); 		$this->printProgress('<b>Уникальных ID пользователей с информации: '.count($usersWithInfo).'</b>');	 		$this->printProgress('Подготавливаю массив с информацией о пользователях...'); 		$this->users = $this->remakeUsersArray($usersWithInfo); 		$this->printProgress('Массив успешно сформирован', false); 			 		$this->printProgress('Начинаем искать репосты у пользователей...'); 		$k = 1; 				 		foreach ($this->users as $id => $data) {			 			$this->printProgress('<i>'.$k.') Обрабатывается пользователь: <a href="http://vk.com/id'.$id.'">id'.$id.'</a> - '.$data['last_name'].' '.$data['first_name'].'</i>'); 			$this->getUsersPosts($id); 			 			if ($this->find) { 				$this->printProgress('Определяем количество репостов #'.$this->findPost.' у пользователя...'); 				$this->getUsers($id, $this->findPost, 'copies', 0, true); 					 				$status = '<span'; 				if ($this->countReposts > 0) $status .= ' style="font-size: 20px;"'; 				$status .= '>Количество репостов #'.$this->findPost.': <b>'.$this->countReposts.'</b></span>'; 					 				$this->printProgress($status, false); 				$this->users[$id]['count_reposts'] = $this->countReposts; 					                                //тут можно добавлять $this->user[$id]  в сессию 			} 				 			$k++;			 		} 			 		$this->printProgress('Поиск репостов завершен', false); 	}  } 

Вышеприведенный класс позволяет получить количество репостов с пользовательскими данными. Подобным образом можно считать и лайки (необходимо лишь установить ?filter=likes)
Данное решение — очень удобный вариант отображения данных при встройке на свой сайт, при проведении различных викторин и конкурсов. К примеру мы это реализовали так

ссылка на оригинал статьи http://habrahabr.ru/post/177641/