Pull to refresh

Проксируем RSS поток при помощи Python

Reading time 5 min
Views 8.5K
Одним из безусловно удобных способов получать новости, статьи и т.п. с различных сайтов — является RSS. Однако с каждым годом число лент неуклонно возрастает, кол-во фидов увеличивается, а времени на разгребание всего этого хозяйства становится все меньше и меньше. Очевидно — нужно как-то автоматически фильтровать статьи. Этим мы сегодня и займемся.


Существующие решения


Была перепробована куча сервисов и офлайн читалок, но идеала найти не удалось. Все десктопные приложения имели на удивление куцый функционал в плане фильтрации. Одну за другой поставил больше десятка различных программ и не нашел ни одной, которая смогла бы предоставить интерфейс для простой задачи — отфильтровать фиды по ссылке на полную статью. Не говоря уже про то, что сделать это хотелось через регулярные выражения. А уж про то, что бы иметь возможность новость отредактировать — и речи не идет. Остановился я на встроенной в оперу читалке. Там имеется возможность указывать определенные правила для фильтрации поступающих фидов. Все худо бедно работало, однако в каком-то билде опера стала мне помечать прочтенными лишние статьи. Охотно верю, что там давно все починили, но доверие было подорвано.

И я обратился за помощью к online сервисам. Существует некоторое число довольно посредственных типа feedrinse.com, которые толком ничего не умеют делать, а что гораздо страшнее — вполне вероятно через месяц\два они неожиданно прекратят свое существование. Однако один из сервисов выделялся из общего ряда очень сильно — yahoo pipes. Он имел казалось бы все что нужно, позволял гибко фильтровать фиды, сливать ленты и много чего еще, однако периодические глюки и тормоза сводят на нет все прелести сервиса. Да и визуальное программирование, как я понял, поковырявшись с pipes, не для меня.

Идеи


Не оставалось ничего иного, как засесть за написание собственного велосипеда.

Сначала думал сделать свою собственную офлайн RSS читалку с удобной и развесистой фильтрацией и прочими плюшками. Я даже написал список требований к программе, некое подобие ТЗ и каркас приложения… Однако довольно быстро понял, что на это уйдет в лучшем случае несколько месяцев работы по вечерам и в результате вряд ли получится что-то существенно более гибкое и удобное, чем существующие аналоги. И идея была отброшена, как непригодная.

Тогда я решил пересмотреть свои требования и выделить главные:
  • максимально гибкая фильтрация фидов
  • возможность изменять содержимое rss ленты
  • возможность использовать интерфейс существующих продуктов

В итоге было решено написать своеобразный прокси, который парсит входящую RSS ленту, представляет каждый ее элемент в виде объекта хранящего все атрибуты фида, затем этот объект передается пользовательской функции, написанной на скриптовом языке. А в функции конечный пользователь уже может делать с полученным объектом все что угодно — фильтровать по любому свойствую фида, менять внутреннее содержимое, добавлять что-то свое. Т.е. никакого ограничивающего визуального интерфейса, только чистое программирование, с потенциально безграничными возможностями. Результатом работы прокси будет rss лента, т.е. фактически xml документ на который мы и будем нацеливать нашу читалку. В качестве языка после недолгих раздумий был выбран Python, как простой и выразительный.

Реализация


Во-первых было решено все подписки на RSS ленты перенести в google reader, получив тем самым неких унифицированный интерфейс и несколько плюшек, таких как: бесконечная история, возможность помечать тегами сообщения и ленты.

Далее — потребовался web сервер с возможностью выполнения скриптов на python. Я пошел по пути наименьшего сопротивления — поднял у себя на машине IIS7 и настроил на нем python (настройка расписана например вот тут). Кто идеологически не приемлет IIS конечно же может взять apache или еще что-то.

Затем для каждой ленты пишем скрипт по фильтрации фидов, я буду все показывать на примере хабра, поэтому файлик я называл habrahabr.py, и положил его в директорию к web серверу, туда же следует положить и небольшую библиотечку, которая является оберткой для google reader api. Все необходимое, вместе с примером, можно скачать отсюда.

Итак, вернемся к скрипту habrahabr.py, он должен иметь примерно такое содержимое:

import re
import lib
import const
import functools

def hook_channel(channel):
	pass

def hook_entry(reg_exclude, entry):
	result = reg_exclude.match(entry._link)
	if result == None:
		return entry
	else:
		return None

def run():
	gr = lib.GReader()
	if not gr.login(const.EMAIL, const.PASSWORD):
		print "login failed"
		return
	
	pattern     = 'http://habrahabr.ru/blogs/(%s)/.*'
	w           = 'javascript|php|Flash_Platform'
	reg_exclude = re.compile(pattern % w, re.IGNORECASE)
	fhook_entry = functools.partial(hook_entry, reg_exclude)
  
	xml = gr.read_tag("habrahabr.ru", 300, hook_channel, fhook_entry)

	print "Content-Type: text/xml"
	print
	print xml

if __name__=='__main__' :
	run()


Тут все довольно просто:

Первым делом коннектимся в Goggle Reader (подробно про аутентификацию в сервисах google для приложений, можно почитать тут), для удобства использования запрос на авторизацию обернут в одну функцию и вызывается так: gr.login(const.EMAIL, const.PASSWORD)

Далее я компилирую регулярное выражение, с помощью которого, я будут по ссылке на полную версию статьи отфильтровать неинтересные для меня фиды.

Затем функцией gr.read_tag(«habrahabr.ru», 300, hook_channel, fhook_entry)
получаем последние 300 статей из gReader лежащих там в папке «habrahabr.ru» (не обязательно обращаться по имени папки, это может быть и произвольный тег), далее передаем два хука:

hook_channel — не особо интересен, он позволяет лишь поменять параметры канала (пока только его заголовок)

fhook_entry — позволяет фильтровать и изменять фиды. В качестве входного параметра в него передается экземпляр класса Entry (из файла lib.py), который собственно и является разобранным фидом, его атрибуты соответствуют атрибутам фида. Отмечу, что любой из этих атрибутов можно произвольным образом менять и в ленту попадет уже измененное значение. Хук обязан возвратить измененный объект класса Entry, либо None, если мы хотим «вырезать» эту запись из ленты.

Функцию read_tag — возвращает xml строку в формате rss v 2.0. Полученную таким образом строку я вывожу на печать, добавляя мета информацию для web сервера.

Ну вот собственно и все, остается только в RSS читалке подписаться на новый адрес, у меня он получился такой: 127.0.0.1:8080/python/habrahabr.py.

Подробно почитать про API gReader можно тут. А вот тут можно найти спецификацию RSS.

Подводя итоги


В скрипте сейчас явно храниться логин и пароль для google аккаунта, в целом для меня это не критично, т.к. всем кто имеет физический доступ к компьютеру я доверяю, а в то что какой-то вирус сможет добраться до этого скрипта и вытащить оттуда пароль — я не верю. Но в любом случае наверное стоит для gReader завести отдельный аккаунт, тогда все проблемы решаться.
В целом скрипт пока получается сыроватым и мало функциональным, но предъявленным мною требованиям отвечает вполне.
В планах добавить в него автогенерацию кнопки для добавления статьи в readitlater или evernote, а так же для фидов с подкастами — авто скачивание подкаста в определенную папочку.
Так же я надеюсь на некоторую обратную связь от хабрасообщества, которая поможет улучшить скрипт.
Tags:
Hubs:
+10
Comments 7
Comments Comments 7

Articles