Анализ дружеских связей VK с помощью Python. Часть первая. Друзья друзей

в 6:56, , рубрики: python, Вконтакте, Вконтакте API, Программирование, Социальные сети и сообщества

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

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

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

Как будем реализовывать:

  • Задаем нужную нам глубину
  • Отправляем исходные данные либо те id, которые надо исследовать на данной глубине
  • Получаем ответ

Что будем использовать:

  • Python 3.4
  • Хранимые процедуры в ВКонтакте

Задаем нужную нам глубину

Что нам потребуется в начале — это указать глубину (deep), с которой мы хотим работать. Сделать можно это сразу в main.py:

print(a.deep_friends(2)) # такая строчка там уже есть

deep равное 1 — это наши друзья, 2 — это друзья наших друзей и так далее. В итоге мы получим словарь, ключами которого будут id пользователей, а значениями — их список друзей.

Не спешите выставлять большие значения глубины. При 14 моих исходных друзьях и глубине равной 2, количество ключей в словаре составило 2427, а при глубине равной 3, у меня не хватило терпения дождаться завершения работы скрипта, на тот момент словарь насчитывал 223.908 ключей. По этой причине мы не будем визуализировать такой огромный граф, ведь вершинами будут ключи, а ребрами — значения.

Отправление данных

Добиться нужного нам результата поможет уже известный метод friends.get, который будет расположен в хранимой процедуре, имеющей следующий вид:

var targets = Args.targets;
var all_friends = {};
var req;
var parametr = "";
var start = 0;
// из строки с целями вынимаем каждую цель
while(start<=targets.length){
    if (targets.substr(start, 1) != "," && start != targets.length){
        parametr = parametr + targets.substr(start, 1);
    }
    else {
        // сразу делаем запросы, как только вытащили id
        req = API.friends.get({"user_id":parametr});
        if (req) {
            all_friends = all_friends + [req];
        }
        else {
            all_friends = all_friends + [0];
        }
        parametr = "";
    }
    start = start + 1;
}
return all_friends;

Напоминаю, что хранимую процедуру можно создать в настройках приложения, пишется она на VkScript, как и execute, документацию можно прочесть здесь и здесь.

Теперь о том, как она работает. Мы принимаем строку из 25 id, разделенных запятыми, вынимаем по одному id, делаем запрос к friends.get, а нужная нам информация будет приходить в словаре, где ключи — это id, а значения — список друзей данного id.

При первом запуске мы отправим хранимой процедуре список друзей текущего пользователя, id которого указан в настройках. Список будет разбит на несколько частей (N/25 — это и число запросов), связано это с ограничением количества обращений к API ВКонтакте.

Получение ответа

Всю полученную информацию мы сохраняем в словаре, например:

{1:(0, 2, 3, 4), 2: (0, 1, 3, 4), 3: (0, 1, 2)}

Ключи 1, 2 и 3 были получены при глубине равной 1. Предположим, что это и были все друзья указанного пользователя (0).

Если глубина больше 1, то далее воспользуемся разностью множеств, первое из которых — значения словаря, а второе — его ключи. Таким образом, мы получим те id (в данном случае 0 и 4), которых нет в ключах, разобьем их опять на 25 частей и отправим хранимой процедуре.

Тогда в нашем словаре появятся 2 новых ключа:

{1:(0, 2, 3, 4), 2: (0, 1, 3, 4), 3: (0, 1, 2), 0: (1, 2, 3), 4:(1, 2, ….)}

Сам же метод deep_friends() выглядит следующим образом:

def deep_friends(self, deep):
		result = {}

		def fill_result(friends):
			for i in VkFriends.parts(friends):
				r = requests.get(self.request_url('execute.deepFriends', 'targets=%s' % VkFriends.make_targets(i))).json()['response']
				for x, id in enumerate(i):
					result[id] = tuple(r[x]["items"]) if r[x] else None

		for i in range(deep):
			if result:
				# те айди, которых нет в ключах + не берем id:None
				fill_result(list(set([item for sublist in result.values() if sublist for item in sublist]) - set(result.keys())))
			else:
				fill_result(requests.get(self.request_url('friends.get', 'user_id=%s' % self.my_id)).json()['response']["items"])

		return result

Конечно, это быстрее, чем кидать по одному id в friends.get без использования хранимой процедуры, но времени все равно занимает порядочно много.

В заключение

Если бы friends.get был похож на users.get, а именно мог принимать в качестве параметра user_ids, то есть перечисленные через запятую id, для которых нужно вернуть список друзей, а не по одному id, то код был бы намного проще, да и количество запросов было в разы меньше.

Данные мы собрали, сегодня на этом все. Далее мы будем с этими данными играться.

Автор: STLEON

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js