Если ты устал видеть в своих фидах лишние посты или хочешь получать вместо заголовков полноценные тексты, то отправляйся со мной — в путешествие, которое сделает тебя повелителем 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

Шифруйся грамотно! Изучаем перспективные мессенджеры для приватной переписки

Тайна переписки заботит не только тру-хакеров, но и миллионы простых пользователей, которы…