...

вторник, 15 июля 2014 г.

Анализ дружеских связей 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, то код был бы намного проще, да и количество запросов было в разы меньше.

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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий