пʼятницю, 10 липня 2009 р.

Mechanize + Nokogiri + вКонтакте. Частина 5


Ніхто не помітив зовнішніх змін. Положення тіла пацієнта вКонтакте на операційному столі залишалось незмінним. Але мушу повідомити неприємну новину: ідентифіковано мутації. Тому всі попередні дослідження [1][2][3][4] вважаються недійсними. Нічого... не треба падати духом.
Їжачки плакали і кололись, але продовжували кохатися з кактусами! ©

Nokogiri - це парсер HTML, XML, SAX. Серед його численних можливостей можна виділити можливість використання XPath(XML Path Language) і CSS3 селекторів для пошуку.
Крім того Nokogiri має сумісний з Hpricot синтаксис XPath та CSS.
На даний час Nokogiri найшвидший парсер XML для Ruby.
Mechanize для синтаксичного розбору html використовує саме Nokogiri.

Приклад використання:

Підключення необхідних бібліотек
require 'nokogiri'
require 'open-uri'

Завантаження HTML сторінки, використовуючи open-uri, який йде з Ruby
doc = Nokogiri::HTML(open('http://www.google.com/search?q=ruby-ua'))

Пошук вузлів за допомогою CSS3 селекторів
doc.css('h3.r a.l').each{|link| puts link.content}

Пошук вузлів за допомогою XPath
doc.xpath('//h3/a[@class="l"]').each {|link| puts link.content}


Автор рекомендує використовувати XPath селектори для вищої швидкодії - пошук CSS швидкий ніж у Hpricot, але не такий швидкий.
Nokogiri::XML::Node#xpath(*paths) - функція пошук вузлів(nodes) для XPath шляхів(paths). Шлях повинен бути одним або декількома XPath запитами(queries).
Цікавоє можливістю є задання власних функцій XPath із використанням регекспів(regexp). Для цього потрібно створити класс і застосувати функцію # яку хочете визначити. Наприклад:

node.xpath('.//tr[mess(., "mess\d+")]', Class.new {
def mess node_set, regex
node_set.find_all { |node| node['id'] =~ /#{regex}/ }
end
}.new)


Така функція знайде всі вузли виду:
<tr id="mess127">[...]</tr>
<tr id="mess128">[...]</tr>
<tr id="mess129">[...]</tr>


вКонтакте!?
У зв'язку зі змінами на сайті vkontakte.ru мої попередні дописи про вконтакте втратили свою актуальність. Не знаю скільки часу буде працездатним цей код. Але суть в іншому: показати принципи роботи з Mechanize і Nokogiri на реальному прикладі.
Що ж робить нищеприведений код?

  • Авторизація через надсилання email-адреси та пароля користувача; (login_force)

  • Отримання останніх приватних повідомлень; (get_messages)


# -*- encoding: utf-8 -*-
require 'mechanize' # recommended 0.9.2

@a = WWW::Mechanize.new{|agent|
agent.user_agent_alias = 'Linux Konqueror'
agent.follow_meta_refresh = true
}

# Authorisation by submitting email and password (Login)
#
def login_force(uid, email, pass)
login_page = @a.get('http://vkontakte.ru/login.php')
login_form = login_page.form_with(:name => "login")
login_form.email = email
login_form.pass = pass
user_page = login_form.submit
return /http:\/\/vkontakte\.ru\/id(\d+)/ === user_page.uri.to_s ? ($1 == uid ? true : false) : false
end

# Getting private messages list for current user
#
# Structure of each entry of resulting array
# [0] - message from
# [1] - message date
# [2] - message title
# [3] - message_body
#
def get_messages
url = 'http://vkontakte.ru/mail.php'
messages_page = @a.get(url)
node = Nokogiri::HTML(messages_page.body)
message = node.xpath('.//tr[mess(., "mess\d+")]', Class.new {
def mess node_set, regex
node_set.find_all { |node| node['id'] =~ /#{regex}/ }
end
}.new)
message_from = message.xpath('//td[@class="messageFrom"]/div[@class="name"]/a').map {|link| /\/id(\d+)/.match(link['href'])[1]}
message_date = message.xpath('//td[@class="messageFrom"]/div[@class="date"]').map {|link| link.content}
message_title = message.xpath('//a[@class="new messageSubject"]/span').map {|link| link.content}
message_body = message.xpath('//a[@class="new messageBody"]').map {|link| link.content}
messages = Array.new
messages = [message_from, message_date, message_title, message_body].transpose
return messages
end

id = "12345678"
email = "username@example.com"
pass = "secret"

if login_force(id, email, pass)
puts "Successfully Authentication"
get_messages.each {|message|
puts "[#{message[1]}]: #{message[0]}"
puts message[2]
puts message[3]
puts "==="
}
else
puts "Authentication Error"
end

1 коментар:

Alex сказав...

в целом очень интересный блог, как и каждая статься в частности!