Анализ дружеских связей VK с помощью Python. Часть первая. Друзья друзей
В предыдущей статье мы на основе общих друзей ВКонтакте строили граф, а сегодня поговорим о том, как получить список друзей, друзей друзей и так далее. Предполагается, что вы уже прочли предыдущую статью, и я не буду описывать все заново.Начнем с того, что просто скачать все 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, то код был бы намного проще, да и количество запросов было в разы меньше.Данные мы собрали, сегодня на этом все. Далее мы будем с этими данными играться.