Если ты устал видеть в своих фидах лишние посты или хочешь получать вместо заголовков полноценные тексты, то отправляйся со мной — в путешествие, которое сделает тебя повелителем RSS и владельцем собственного сервиса синхронизации.

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

 

Задумка

Мысли о том, что хорошо бы как-нибудь настроить фильтрацию RSS, я вынашивал давно — практически все время, что я пользуюсь агрегаторами (то есть примерно со времен появления Google Reader и яндексовской «Ленты»; ныне оба уже не работают). Возможность зафильтровать элементы фида по ключевым словам мне попалась в маковском клиенте ReadKit (подробности — в моем обзоре за 2014 год), но я предпочитаю Reeder, к тому же фильтры должны работать на серверной стороне, иначе клиенты для телефона и планшета останутся в пролете.

Временным решением стал переход с Feedly, который я использовал в качестве бесплатного бэкенда, на Inoreader — замечательный сервис, разработанный крайне мотивированной и душевной польской командой (о нем я, кстати, тоже уже писал). В платной версии Inoreader есть поддержка фильтров (до 30 штук) и другие приятные фичи.

Создание фильтра в Inoreader
Создание фильтра в Inoreader

Однако тариф с фильтрами обходится в 30 долларов за год, что сравнимо с ценой недорогого хостинга. А это уже наводит на вполне конкретные мысли: нельзя ли сделать собственный аналог и вместо встроенных фильтров, логика которых ограничена, писать любые скрипты?

Проще всего, наверное, было бы написать пару скриптов, которые бы по расписанию забирали, обрабатывали и выкладывали нуждающиеся в фильтрации фиды на какой-нибудь хостинг, а тот же Feedly их бы оттуда подхватывал. Однако, наткнувшись на проект Coldsweat, я не мог устоять.

Coldsweat — это опенсорсный клон Fever, платного средства синхронизации RSS, которое предназначено для установки на свой сервер. Собственно, существование протокола Fever и делает Coldsweat удобным: поддержка Fever API есть в некоторых продвинутых агрегаторах, в том числе в Reeder. Coldsweat написан на Python, использует базу данных SQLite (по желанию можно настроить PostgreSQL или MySQL), имеет систему плагинов и веб-интерфейс. То, что нужно!

Веб-интерфейс Coldsweat
Веб-интерфейс Coldsweat
 

Приготовления

Вот список того, что понадобится для развертывания Coldsweat.

  • Дистрибутив Coldsweat. Скачай или клонируй его с GitHub.
  • Сервер с UNIX или Linux и как минимум доступом к cron и .htaccess (лучше, конечно, полный шелл).
  • Python 2.7, желательно не младше 2.7.9. С Python 3.x Coldsweat не заведется.
  • Библиотеки Peewee, Requests, WebOb и Tempita. Все они перечислены в файле requirements.txt, так что можешь просто написать pip install -r requirements.txt (в системе для этого должны быть команды pip и easy_install из пакета python-setuptools).
  • Библиотека Flup — на сервере она понадобится в том случае, если ты будешь использовать FastCGI (а это рекомендуется); для локального тестирования она не нужна.
 

Установка

Скачав Coldsweat и установив зависимости, ставим его, как написано в инструкции. Для начала копируем конфиг из файла с примером:

$ cp etc/config-sample etc/config

Забегая вперед, скажу, что у меня Coldsweat со стандартным конфигом не заработал, причем Python падал без объяснения причин. Проблемой, как оказалось, была многопоточность, так что рекомендую для начала выключить ее. Для этого открой etc/config, найди строку ;processes: 4, убери точку с запятой и поменяй 4 на 0. Заодно можешь глянуть на остальные настройки.

Возвращаемся в корень проекта и выполняем команду

$ python sweat.py setup

Скрипт попросит данные для учетки, после чего создаст базу данных. Если твоим сервером будет пользоваться кто-то еще, посмотри в инструкции, как регистрировать дополнительных пользователей.

Теперь импортируем файл OPML со списком фидов. Для тестирования автор Coldsweat рекомендует взять subscriptions.xml из каталога coldsweat/tests/markup/, но лучше лишний раз не мусорить в базе и сразу добавлять актуальный список.

$ python sweat.py import путь/к/файлу.xml

Проверяем, забираются ли фиды:

$ python sweat.py fetch

Если все настроено правильно, то через какое-то время скрипт завершится со словами «Fetch completed. See log file for more information». Если запустить sweat.py с параметром serve, то на порту 8080 у тебя заработает тестовый веб-сервер. Можешь сразу подключить к нему агрегатор, чтобы было удобнее тестировать.

 

Есть одна проблема

Перейдя с того же Feedly на свой сервер, мы лишились возможности добавлять фиды через агрегатор и перекладывать их из папки в папку перетаскиванием. Reeder, к примеру, поддерживает такие функции для Feedly и Inoreader, но не для Fever. Наводить порядок теперь придется через веб-интерфейс Coldsweat, а добавлять фиды можно будет букмарклетом (когда поставишь Coldsweat на свой сервер, нажми на кнопку + в левой панели веб-интерфейса и перетащи букмарклет оттуда на панель браузера).

 

Пишем простой плагин

Синхронизация и скачивание фидов работает, а значит, мы уже можем сделать всякие интересные штуки. Coldsweat поддерживает плагины, так что попробуем воспользоваться ими в своих целях. Вот пример совсем простого плагина, который следит за поступлением комиксов Cyanide & Happiness, заходит на страницы по ссылкам и перекладывает комиксы оттуда в сам фид (подразумевается, что необходимый RSS уже добавлен в список).

import urllib2, re
from coldsweat import *
from coldsweat.plugins import *

@event('entry_parsed')
def entry_parsed(entry, parsed_entry):
  if entry.feed.title == 'Cyanide & Happiness':
    request = urllib2.Request(entry.link)
    page = urllib2.urlopen(request).read()

    m = re.search(r'<img id="main-comic" src="//(.+)\?', page)

    if m is not None:
      entry.content = '<img src="' + m.group(1) + '">'

Здесь используется декоратор @event, чтобы функция entry_parsed вызывалась каждый раз, когда заканчивается парсинг записи. Еще существуют события fetch_started и fetch_done — они срабатывают, соответственно, когда начинается или заканчивается процесс агрегации фидов. Если при написании плагинов тебе понадобится знать структуру объектов типа Entry или Feed, то можешь подсмотреть их в файле models.py.

Чтобы плагин заработал, сохрани его в папку plugins, к примеру, под именем cyanide.py, а затем найди в etc/config секцию [plugins] и впиши туда строчку load: cyanide. Все последующие плагины будут перечисляться дальше через запятую.

Продолжение доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все материалы на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи один материал

Заинтересовала информация, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для материалов, опубликованных более двух месяцев назад.


9 комментариев

  1. d13ma

    10.12.2016 at 14:47

    Чем например InoReader хуже? Просто столько телодвижений когда уже есть готовые сервисы…

    • Андрей Письменный

      Андрей Письменный

      10.12.2016 at 17:24

      — Inoreader имеет ограниченную логику фильтров;
      — не позволяет запрашивать страницы целиком для анализа или включения в фид;
      — в версии с фильтрами стоит столько же или дороже своего сервера, который можно параллельно использовать для других вещей;
      — никто не знает, когда он закроется подобно многим другим мелким сервисам (да и крупным, если вспомнить тот же Google Reader)
      — сооружать своё забавнее 🙂

      • d13ma

        10.12.2016 at 22:07

        До закрытия заранее объявят как это было с упомянутым ридером, всегда можно сделать экспорт и импорт…
        Ну видимо я просто не понимаю смысла, хватает бесплатного иноридера.

  2. coolparty

    12.12.2016 at 00:08

    Что-то у меня не получилось на сервере запустить

    File «/var/www/html/coldsweat/plugins/mercury.py», line 11, in
    for feed in feeds :
    NameError: name ‘feeds’ is not defined

    • Андрей Письменный

      Андрей Письменный

      12.12.2016 at 00:17

      Лежит ли файл mercury-feeds.json на своем месте в etc внутри coldsweat?
      Можно на сервере запустить python и попробовать вручную набрать:
      import json
      feeds = json.loads(open(‘путь-к-coldsweat/etc/mercury-feeds.json’).read())
      feeds
      и посмотреть, не будет ли ошибок.

  3. alxchk

    17.12.2016 at 08:08

    Но почему не tt-rss?

  4. alxchk

    17.12.2016 at 13:48

    Есть плагины — https://github.com/dasmurphy/tinytinyrss-fever-plugin
    Я не пользуюсь iOS, так что не знаю как оно. Для андроида его родной клиент хороший. Фильтры классные, все можно конвертировать в собственный фид..

    • Андрей Письменный

      Андрей Письменный

      30.12.2016 at 11:38

      Что ж, остается только одна причина — я не люблю PHP 🙂
      Но на самом деле Coldsweat попался мне первым и имел почти всё, что нужно. Логику фильтров и скрэперы я всё равно собирался написать сам, так что сложности кажутся вполне естественным побочным эффектом.

Оставить мнение

Check Also

Микросервисы по-микрософтовски. Пакуем приложения ASP.NET Core с помощью Docker

Кажется, Microsoft все больше и больше любит Linux! Приложения ASP.NET Core теперь по-наст…