Друзья друзей Вконтакте на Ruby 23 сентября 09
Захотелось мне написать нечто непосредственно полезное.
Немного логики
Если несколько твоих знакомых знакомы с Васей Пупкиным, то с большой вероятностью ты тоже с ним знаком. Логично?
Реализация
На «Моем круге», например есть такое понятие, как «второй круг» – друзья твоих друзей. На Вконтакте «второй круг» просто так не посмотришь. То есть можно, конечно, лазить по друзьям и высматривать знакомые лица (если Лицо не поставило идиотскую аватарку) и имена (если Лицо не пытается анонимизироваться). У меня на это нет времени.
Задача: написать скрипт, который собирает «второй круг» автоматически и сортирует по числу общих знакомых.
Много букoв?
Можешь просто скачать работающий скрипт с Github. В него только нужно дописать свои собственные логин/пароль. Кроме того, пригодятся гемы: mechanize, json, haml.
Я бы сделал онлайн-версию, но, боюсь, паранойя помешает тебе им воспользоваться.
Авторизация
Над задачей автоматизированного логина в контакт бьются многие. Тысячи их. На Ruby эта задача решается буквально в пять строчек. Встречайте Mechanize:
# создаем наш виртуальный браузер agent = WWW::Mechanize.new # получаем форму логина login_form = agent.get('http://vkontakte.ru').form('login') # параметры авторизации... login_form.email = 'vas@pupkin.com' login_form.pass = 'gfhjkm' # авторизуемся agent.submit( agent.submit(login_form, login_form.buttons.first).forms.first )
Mechanize – это библиотека, реализующая доступ к сайтам с сохранением состояния – в первую очередь, куков. Как браузер. Еще она позволяет обрабатывать получаемые страницы, например, доставать из них формы и ссылки, или произвольные элементы с помощью CSS-селекторов и XPath. Рекомендую.
Последняя строчка скрипта такая же странная, как и то, что после логина тебя перебрасывает на какую-то страницу, а потом уже на твой профиль. Там на самом деле производится отправка еще одной формы, да.
Разбор друзей
Не поверишь, но Контакт отдает список друзей в json в теле страницы friends.php. Ура! Не надо будет парсить HTML.
# достаем страницу с друзьями... friends_page = agent.get('http://vkontakte.ru/friends.php') # ...и выкусываем из нее строчку с json-объектом. friends_json_string = friends_page.body.match(/^\s+var friendsData = (\{.+\});$/)[1] # получаем хеш friends_json = JSON.parse friends_json_string # OH SHI...
Не совсем ура: json этот почему-то неправильный и средствами Ruby не парсится. Проверяем строчку валидатором JSON Lint. Обнаруживаем (пока) три проблемы: 1) одинарные кавычки; 2) числовые ключи без кавычек; 3) некорректные символы. Все это исправляется: да, я знаю, так можно попортить значения, но для данной задачи это неважно
friends_json_string = friends_json_string.gsub("'","\"").gsub(/(\d+):/,"\"$1\":").gsub(/[\x00-\x19]/," ") friends_json = JSON.parse friends_json_string # profit!
Анализ
Дальше все просто – собираем второй круг, для каждого товарища оттуда считаем количество общих знакомых, сортируем и выводим. Дальше идет более или менее псевдокод.
# {id => name, ...} names = {} # [id, id,...] my_friends = [] # {id => [id, id, id], ...} common_friends = {} # get_friends - это такая функция, которая возвращает массив типа [{:id => id, :name => name}, ...] get_friends.each do |friend| # собираем своих друзей my_friends << friend[:id] # запоминаем имена для удобства names[friend[:id]] = friend[:name] end my_friends.each do |friend_id| get_friends(friend_id).each do |his_friend| # добавляем друга в список общих знакомых common_friends[his_friend[:id]] ||= [] common_friends[his_friend[:id]] << friend_id # опять-таки запоминаем имя names[his_friend[:id]] = his_friend[:name] end end # отбрасываем своих друзей, а также людей, у которых только один общий знакомый common_friends.reject! {|id,friends| my_friends.include?(id) || (friends.length < 2)} # сортируем оставшихся по количеству общих знакомых common_friends.sort! {|a,b| b[1].length <=> a[1].length}
Все! Остается вывести красиво отформатированный список people. Конечно, используя haml.
!!!
%html
%body
%h1="Total people in 2nd circle: #{people.length}"
%ul
-people.each do |id,friends|
%li
%div
%a{ :href=>"http://vkontakte.ru/id#{id}"}=names[id]
="(#{friends.length})"
%div
=friends.map{|id| names[id]}.join ', 'Заключение
Есть мысль сделать более удобную библиотеку для доступа к данным Вконтакте, только я сомневаюсь в полезности такой библиотеки. Разве что проводить всякий статистический анализ, или автоматически рассылать сообщения/приглашения/спам.
Если кому-нибудь такое интересно, пишите в комменты.

Подписаться на RSS

Комментарии
Я понимаю что статья скорее ознакомительная чем реальная но если захотите продолжать развивать тему парсинга вконтакта можете продолжить один из существующих проектов: http://github.com/oleganza/vkontakte.ruby
или форкните и докручивайте то, что нужно
Вашу библиотеку я видел, но мне хочется чего-то объектного, в духе ActiveResource. Если уж делать библиотеку, то есть: я пока не вижу ей применения.
У меня почему-то не работает скрипт – ошибка:
/home/alex/vk.rb:56: undefined method `each’ for nil:NilClass (NoMethodError)
Что подскажете?
Странно, скрипт почему-то не нашел список ваших друзей. А на парсинг JSON он не ругался?
кажется, в первый раз налажал, и не ту версию скрипта запускал, без пароля и мыла.
В любом случае, сейчас выдает такое
50
49
…
2
1
(haml):13:in `render’: compile error (SyntaxError)
(haml):13: syntax error, unexpected $end, expecting kEND
…l_very_temp));}\n», 0, false);
^
from /usr/lib/ruby/gems/1.8/gems/haml-2.2.5/lib/haml/engine.rb:167:in `render’
from /usr/lib/ruby/gems/1.8/gems/haml-2.2.5/lib/haml/engine.rb:167:in `instance_eval’
from /usr/lib/ruby/gems/1.8/gems/haml-2.2.5/lib/haml/engine.rb:167:in `render’
from /home/alex/vk.rb:81
from /home/alex/vk.rb:81:in `open’
from /home/alex/vk.rb:81
Может, это из-за копипасты? Haml очень чувствителен к пробелам. Github позволяет скачать скрипт и в чистом виде – http://github.com/leonid-shevtsov/vkontakte-friends-parser/raw/master/parser.rb (кроме того, я добавил в него немного проверок)
Привет, Лёнчик! А будет ли эта штука работать если твой друг закрыл свой список друзей?
Никак не будет – таких друзей она игнорирует.
Сорри за оффтоп, Леня, может напишешь статейку о Haml и Sass? Думаю начать использовать это, правда для пыха. Не уверен еще в полезности затеи.
Спасибо Леонид!
Давно интересовался такой темой.
дважды приятно, что тоже днепропетровец.
Вот, кстати, подобные разработки еще одного парня
http://ruby-ua.blogspot.com/