Huginn, Yahoo Pipes, Habr и фид ленты комментариев

Huginn Agent event flow

Понадобилось мне получать с Хабра ленту комментариев пользователя в каком-то удобном виде, чтобы не заходить периодически на сайт для проверки её обновления. Очевидное решение — RSS.

В базе Хабр позволяет подписаться на профиль пользователя, или, по RSS, на ленту его публикаций. А вот для комментариев подобной возможности не предусмотрено (сама лента комментариев имеет вид https://habr.com/ru/users/user-name/comments/).

Первая мысль — воспользоваться Yahoo Pipes, ведь когда-то уже использовал его для решения подобных задач. Увы — оказалось, что сервис давно закрыт. Помолчим минуту (он был хорош!) и рассмотрим доступные альтернативы.

Интернет предлагает несколько бесплатных (или имеющих бесплатные тарифные планы) вариантов, например:

  • IFTTT — слишком абстрактно для моей задачи (первым шагом надо выбрать сервис, для которого у них есть готовые наборы правил обработки), плюс сразу предлагают привязать карту (для активации триала, хотя на стартовой странице обозначен и free plan). И свой триггер можно добавить только к сервису, с которым у IFTTT уже есть интеграция.
  • pipe2py — предлагается разворачивать в Google App Engine, для чего нужно привязать карту, а сделать это в данный момент непросто. Слишком сложно для «просто попробовать и сравнить».
  • Zapier — идеалогически похож на IFTTT — тоже надо выбирать готовое «приложение», которое уже есть в сервисе. Это не решение моей задачи.
  • dlvr.it — тут всё понятно из их самопрезентации — Automate Social Media Posting — полностью мимо.
  • Huginn — наконец — наш сегодняшний герой. Автономный (поддерживает локальное развертывание), гибкий (даже готовых обработчиков хватит для многого), скромный (в части интерфейса по сравнению с вышеперечисленными). Надо пробовать!

Сразу отмечу, что ни одно из приведённых решений (даже Huginn) не является аналогом Pipes, который был удобен своей возможностью комбинировать «блочную сборку» с тонкой настройкой алгоритма работы блока (если это требовалось). Однако — пора познакомиться с Huginn поближе.

Первое знакомство с Huginn

Установка

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

Локальный docker

При локальной установке имеет смысл примонтировать хранилище в отдельный том (или каталог в хост-системе), который нужно предварительно создать: docker volume create huginn-mysql. А также — примонтировать .gitignore следующего содержания:

[safe]
        directory = /app

чтобы избежать лишней «ругани» в логах (при развертывании контейнера идёт обновление сабмодулей из репозитория).

После чего можно создать контейнер (на первый раз — с перенаправлением вывода контейнера в консоль):

docker run -it -p 3000:3000 -v huginn-mysql:/var/lib/mysql \
    --mount type=bind,source="${HOME}"/dev/docker/gitconfig,target="/app/.gitconfig" \
    --name huginn huginn/huginn

и в дальнейшем запускать его через docker start huginn.

Внешний docker на GitHub

Среди доступных в настоящий момент сервисов, предлагающих бесплатные ресурсы для развертывания приложения, GitHub Godespaces — один из немногих. Что немаловажно — в нём можно просто развернуть контейнер docker, без необходимости писать сборочные скрипты.

Кстати, доступны весьма неплохие, для бесплатного предложения, варианты машин:

Github Codespaces free plan

Достаточно в панели управления выбрать шаблон Blank, дождаться запуска сервиса, и в открывшейся консоли запустить докер: docker run -it -p 3000:3000 huginn/huginn.

Порты пробрасываются автоматически, после запуска приложение можно открыть в браузере (логин/пароль по умолчанию: admin / password). Чтобы приложение было доступно «извне», а не только внутри аккаунта github, нужно переставить порт на Public (правой кнопкой на Private -> Видимость порта -> Public).

Настало время перейти к главному — тому, ради чего всё затевалось — преобразованию страницы Хабра в RSS.

Парсинг сайта в Huginn

Простой вариант создания RSS

В простейшем случае — достаточно комбинации трёх агентов (так называются обработчики в Huginn), а именно:

  • Website Agent — для получения исходных данных (страницы ленты) с сайта
  • Event Formatting Agent — для подготовки (очистки) полученных данных
  • Data Output Agent — вывод результата в RSS (xml) и json

но сначала стоит добавить новый сценарий, в котором эти агенты будут сгруппированы: Scenarios -> New scenario. На открывшейся после создания нового сценария странице можно смело жать New agent. Настройки агентов достаточно подробно документированы на странице их создания, подробно останавливаться не буду.

Website Agent

Выбрать тип Website Agent, дать ему произвольное название, добавить его к созданному сценарию, затем, в разделе Options, переключить вид настроек (Toggle View) и вставить туда код агента (не забудьте в ссылке поменять hbn3 на интересующего вас автора).

{
  "url": "https://habr.com/ru/users/hbn3/comments/",
  "mode": "on_change",
  "expected_update_period_in_days": "30",
  "extract": {
    "url": {
      "css": "a.tm-comment-footer__button",
      "value": "@href"
    },
    "title": {
      "css": "a.tm-user-comments__header-link",
      "value": "string(.)"
    },
    "body_text": {
      "css": "div.tm-comment__body-content",
      "value": "./node()"
    },
    "publish_date": {
      "css": "a.tm-article-comment__link",
      "value": "string(.)"
    },
    "author": {
      "css": "a.tm-user-info__username.router-link-active",
      "value": "string(.)"
    }
  }
}

Проверить работу агента можно, запустив режим Dry Run. Если всё нормально — откроется вкладка Events с результатами, в случае проблем — подробности будут на вкладке Log.

Event Formatting Agent

Аналогично — добавить новый агент соответствующего типа, вставить код, в качестве источника данных указать созданный на прошлом шаге Website Agent (выбрать по названию в выпадашке).

{
  "instructions": {
    "message": "<p>{{body_text}}<\/p>",
    "published": {
      "date": "{{pretty_date.date}}",
      "time": "{{pretty_date.time}}",
      "raw_date": "{{publish_date  | lstrip | rstrip}}"
    },
    "author": "{{author | lstrip | rstrip}}",
    "link": "https://habr.com{{url}}",
    "title": "{{title}}"
  },
  "matchers": [
    {
      "path": "{{publish_date  | lstrip | rstrip}}",
      "regexp": "(?<date>\\d{2}\\.\\d{2}\\.\\d{4})\\s.\\s(?<time>\\d{2}:\\d{2})",
      "to": "pretty_date"
    }
  ],
  "mode": "clean"
}

Для проверки работы этого агента — перед запуском Dry Run необходимо однократно запустить (Run) агент предыдущего шага, чтобы сгенерировать подлежащие обработке события.

Data Output Agent

Наконец, последний в этой цепочке — Data Output Agent, предназначенный для генерации RSS/JSON фида. Создается тем же способом, источником данных для него служит Event Formatting Agent с прошлого шага. Код агента:

{
  "secrets": [
    "habr-ucf"
  ],
  "expected_receive_period_in_days": "1",
  "template": {
    "title": "Habr comments feed",
    "description": "Comments feed of Habr user",
    "item": {
      "title": "{{title}}",
      "description": "{{message}}",
      "link": "{{link}}",
      "pubDate": "{{published.date}} {{published.time}}",
      "dc:creator": "{{author}}"
    },
    "language": "ru"
  },
  "ns_media": "true"
}

secrets здесь содержит уникальную часть url-адреса будущего фида.

Результат

Наконец — можно запустить Website Agent и дождаться прохождения событий по цепочке агентов (это происходит в пределах минуты-другой, также есть возможность вызвать принудительное распространение событий). В итоге должна получиться следующая связь из трёх агентов:

Из диаграммы видно, что из источника (Хабр) было отобрано на основе правил 20 записей, которые затем были отформатированы и трансформированы в ленту.

Адреса фидов можно найти на вкладке Summary агента Data Output Agent.

Если видимость проброшенного порта объявлена, как Public — на ленту можно подписаться, не логинясь в аккаунт Github.

Продвинутый вариант создания RSS

Чтение ленты комментариев одного пользователя — это уже неплохо. Но что делать, если есть потребность читать нескольких? Здесь также возможны варианты:

  1. Простой вариант — скопировать всю цепочку агентов. Можно даже в отдельный сценарий, ещё проще — экспортировать существующий сценарий, переименовать его и агенты (в полученном при экспорте файле), затем — импортировать обратно в Huginn. Поменять ссылку-источник на нужную, пользоваться. Минусы — игнорирование DRY, повышается нагрузка приложение. Ленты разных пользователей (источников) сливаются в одну общую, которой в дальнейшем неудобно пользоваться. Захламляется пространство сценариев. Плюсы — крайне просто реализуется, отключить ставшую ненужной ленту можно через управление сценарием.
  2. Сбалансированный вариант (на нём остановлюсь подробней) — создать источники данных для разных ссылок, после обработки — разделить поток на отдельные для каждого источника ленты . Минусы — тот же DRY, чуть сложней в реализации. Плюсы — при необходимости можно добавлять источники в виде Website Agent простым клонированием самого агента с подстановкой в ссылку имени интересующего пользователя. В отсутствие необходимости в источнике — можно просто отключить соответствующий Website Agent.
  3. Экономичный вариант — использовать один общий Website Agent для нескольких ссылок. Минус этого варианта — для добавления/отключения источников надо или редактировать настройки Website Agent, или выключать агенты-потребители ниже по цепочке (при этом сохранятся запросы уже не нужных страниц). Плюс — не приходится создавать «лишние» агенты, реализуется такой вариант несложно (на базе второго).
"url": [
    "https://habr.com/ru/users/hbn3/comments/",
    "https://habr.com/ru/users/AntonyN0p/comments/"
  ],

Сбалансированный вариант

На время опытной эксплуатации (пока Huginn у меня работает в развёрнутом локально контейнере) я остановился на варианте 2. Подробно расписывать его реализацию не буду, скажу лишь, что в цепочку добавляются Trigger Agent для разбивки ленты на отдельные потоки по авторам. Схема, в этом случае, выглядит следующим образом:

Готовый сценарий с набором агентов для второго варианта привожу в виде файла и в виде ссылки на Github. Имена целевых авторов хранятся в Credentials:

При развертывании Huginn на хостинге — планирую перейти на третий вариант формирования лент (с некоторыми доработками — чтобы список ссылок создавался на основе тех же Credentials), о чём незамедлительно напишу :). А пока — приятного (и полезного) оHuginnения!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *