September 23 2009 data scraping Mechanize Ruby Вконтакте
Захотілося мені написати щось безпосередньо корисне.
трохи логіки
Якщо кілька твоїх знайомих знайомі з Васею Пупкіним, то з великою ймовірністю ти теж з ним знайомий. Логічно?
Реалізація
На «Моєму колі», наприклад є таке поняття, як «друге коло» - друзі твоїх друзів. На Вконтакте "друге коло" просто так не подивишся. Тобто можна, звичайно, лазити по друзях і виглядати знайомі обличчя (якщо Особа не поставило ідіотську аватарку) і імена (якщо Особа не намагається анонімізувати). У мене на це немає часу.
Завдання: написати скрипт, який збирає «друге коло» автоматично і сортує по числу спільних знайомих.
Багато букoв?
можеш просто скачати працює скрипт з Github . У нього тільки потрібно дописати свої власні логін / пароль. Крім того, знадобляться геми: mechanize, json, haml.
авторизація
Над завданням автоматизованого логіна в контакт б'ються багато. тисячі їх . На Ruby це завдання вирішується буквально в п'ять рядків. Зустрічайте Mechanize :
# Створюємо наш віртуальний браузер agent = WWW :: Mechanize .new # отримуємо форму логіна login_form = agent. get ( 'http://vkontakte.ru') .form ( 'login') # параметри авторизації ... login_form. email = '[email protected]' 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 ','
висновок
Є думка зробити більш зручну бібліотеку для доступу до даних Вконтакте, тільки я сумніваюся в доцільності такого бібліотеки. Хіба що проводити всякий статистичний аналіз, або автоматично розсилати повідомлення / запрошення /.
Якщо кому-небудь таке цікаво, пишіть в коменти.
Сподобався пост? почастуєте кави
Логічно?Багато букoв?
Include?
Сподобався пост?