Cyxapeff Blog

Rss с drive2

Мне очень нравится сайт drive2.ru. Отличное сообщество, красивые машины и интересные обсуждения. Но разработчики сайта не позаботились о том, чтобы отдавать ленту событий в RSS. Пришлось снова писать костыли. На этот раз костыль я решил написать не на привычном и горячо любимом питоне, а на руби.

Распарсить ленту не представляет труда, а вот авторизоваться на сайте с ходу не получится. На форму входа навешен javascript и без него "войти" невозможно. Мне известны два способа как решить эту проблему. Во-первых, использовать какую-нибудь библиотеку, которая работает с настоящим браузером. Соответственно и javascript и всё остальное там прекрасно работает. Для руби есть, например, Watir. Во-вторых, можно своему скрипту подсунуть рабочие куки и таким образом вообще пропустить стадию авторизации. Собственно этот второй путь я и выбрал.

Моим повседневным браузером является firefox, который хранит куки в базе данных SQLite, которая лежит в ~/.mozilla/firefox/%profile%/cookies.sqlite (путь для линукса, в windows очень похожий). Откроем этот файл и посмотрим что там внутри:

$ sqlite3 /tmp/cookies.sqlite
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
moz_cookies
sqlite> .headers on
sqlite> select * from moz_cookies limit 3;
id|name|value|host|path|expiry|lastAccessed|isSecure|isHttpOnly
1254769091658566|PREF|моя кука|.google.com|/|1326648773|1264938324355994|0|0
1254769091658706|NID|моя кука|.google.com|/|1280320725|1264938264007374|0|1
1254769091832306|PREF|моя кука|.google.ru|/|1317842867|1264938309932153|0|0

Отлично. Значит мы без труда может вытащить рабочие куки из нашего браузера. Но браузер работает на десктопе, а скрипт должен работать на сервере, чтобы постоянно мониторить обновления ленты. Следовательно нужно каким-то образом держать на сервере актуальную базу. Я для этого решил использовать Dropbox, т.к он и так постоянно запущен как на сервере, так и на десктопе. Кидаю ссылку в директорию дропбокса и готово.

Ну а теперь собственно сам скрипт.

# coding: utf-8

require 'sqlite3'
require 'net/http'
require 'nokogiri'
require 'rss/maker'

class Parser
    TITLE = "Drive2.ru Feed"
    LINK = "http://www.drive2.ru/my/feed"
    DESCRIPTION = "Rss from my feed"
    def initialize(sqlfile = nil, txtfile = nil)
        if sqlfile
            @doc = Nokogiri::HTML(get_page(sqlfile))
        elsif txtfile
            @doc = Nokogiri::HTML(get_from_file(txtfile))
        else
            exit()
        end
    end

    def get_page(sqlfile)
        cookies = ''
        `cp #{sqlfile} /tmp/cookies.sqlite`
        db = SQLite3::Database.new('/tmp/cookies.sqlite')
        db.execute( "select name, value from moz_cookies where host = '.www.drive2.ru'" ) do |row|
             cookies += row[0] + '=' + row[1] + ';'
        end

        con = Net::HTTP.new('www.drive2.ru', 80)
        path = '/my/feed/'
        headers = { 'Cookie' => cookies,
                    'Refer' => 'http://www.drive2.ru',
                    'User-Agent' => 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20100105 Shiretoko/3.5.7'}
        res = con.get(path, headers)

        res.body
    end

    def get_from_file(txtfile)
        f = File.new(txtfile)
        f.read()
    end

    def login?
        title = @doc.css('title').first.content.strip
        return true if title == 'Лента'
        return false
    end

    def parse
        result = Array.new
        @doc.css('.journalentries').each do |entry|
            time = entry.css('.eventtime').first.content
            time = Time.parse(modify_time(time))
            title = entry.css('h3').first.content
            content = entry.css('div:regex("j\d+")', Class.new {
                def regex node, regex
                    node.find_all { |node| node['id'] =~ /#{regex}/ }
                end
            }.new).first.inner_html
            link = entry.css('.commentslink > a').first['href']
            result << { :title => title,
                        :datetime => time,
                        :content => content,
                        :link => 'http://www.drive2.ru' + link}
        end
        return result
    end

    def create_rss(entries)
        content = RSS::Maker.make('2.0') do |m|
            m.channel.title = TITLE
            m.channel.link = LINK
            m.channel.description = DESCRIPTION
            m.items.do_sort = true # sort items by date

            entries.each do |entry|
                i = m.items.new_item
                i.title = entry[:title]
                i.link = entry[:link]
                i.date = entry[:datetime]
                i.description = entry[:content]
            end
        end
        return content
    end

private
    def modify_time(string)
        dict = { 'января в' => 'Jan', 'февраля в' => 'Feb',
                 'марта в' => 'Mar', 'апреля в' => 'Apr',
                 'мая в' => 'May', 'июня в' => 'Jun',
                 'июля в' => 'Jul', 'августа в' => 'Aug',
                 'сентября в' => 'Sep', 'октября в' => 'Oct',
                 'ноября в' => 'Nov', 'декабря в' => 'Dec',
                 'вчера в' => (Time.now - 60*60*24).strftime("%d %b")}
        dict.each do |key, value|
            string.sub!(key, value)
        end
        return string
    end
end

if __FILE__ == $0
    parser = Parser.new('/home/smacker/Dropbox/cookies.sqlite')
    if parser.login?
        rss = parser.create_rss(parser.parse)
        f = File.open('/var/www/media/drive2.rss', 'w')
        f.write(rss)
        f.close()
     end
end

Если код не очень красив, не сердчайте, я только начал изучать ruby. В методе get_page я копирую базу в /tmp, потому что пока firefox запущен, он блокирует базу и не даёт из неё ничего читать. Готовый rss файл просто кладётся в директорию доступную по http, а эту ссылку я добавляю в свой Google Reader. Ну а скрипт соответственно прописан в кроне и выполняется раз в 2 часа. Надеюсь, он пригодится кому-нибудь ещё.

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

[HTML_REMOVED]Урок в Вовочкином классе. Детишки рассказывают. Петя: - Мой папа ГАИшник, денег у нас много, живем в достатке. Маша: - Моя мама проститутка, денег у нас много, живем в достатке. Вовочка: - Мой папа дальнобойщик. Если бы не ГАИшники и проститутки, то денег у нас было бы много и жили бы мы в достатке. [HTML_REMOVED] Общество Мегаполис Pi7.ru порадовала новым выходом очередного сборника нюансов. Меня удивила например это "[HTML_REMOVED]Бесплодие разрушает всю мою жизнь! [HTML_REMOVED]" - Канечно вы можете найти и для себя бездну интерестного Ну а однако лучшее снадобье от скуки это анекдотец. [HTML_REMOVED][HTML_REMOVED]http://www.my.pi7.ru/images/users/photos/medium/a60f8906f1ca292298f2a30ce89a2331.jpg[HTML_REMOVED][HTML_REMOVED]

Olyashicks 22.08.2010 - 10:35 #
Markdown syntax:

> цитата           *курсив*
> цитата           **жирный**

* список           1. список
* список           2. список
* список           3. список

отступ в 4 пробела:
    def some_code():
        return "code"
    print some_code()

[ссылка](http://example.com/)
Ваш ник:
E-mail:
или OpenID
Оставьте свой комментарий:
Получать уведомления о новых комментариях