Перейти к содержимому

Мониторинг изменений страницы (Content monitor)

Контент-монитор следит не за доступностью ресурса, а за содержимым: Notifly раз в N секунд загружает заданный URL, извлекает из ответа интересующее значение и сравнивает его с предыдущим. Как только значение изменилось — приходит alert. Это активный монитор (звонит сам Notifly), но он отвечает на вопрос «что изменилось на странице», а не «жив ли сайт».

Типичные задачи:

  • Цена — отслеживать цену товара на странице магазина или у конкурента.
  • Статус / наличие — «в наличии» ↔ «нет в наличии», статус заказа, статус сервиса.
  • Контент конкурента — появилась новая запись в блоге, изменился прайс, обновился changelog.
  • JSON API — значение поля в публичном JSON-ответе (курс, остаток, версия).

Что именно сравнивать, задаёт режим (mode). Поддерживаются пять режимов:

modeЧто извлекаетсяПоле selector
hashSHA-256 всего тела ответа — alert при любом изменении страницыне нужен
jsonЗначение по JSONPath из JSON-ответаобязателен — JSONPath, например $.price или $.items.0.status
textПервое совпадение регулярного выражения (синтаксис Go) в тексте ответаregex; если пуст — ведёт себя как hash
cssТекст первого элемента по CSS-селекторуобязателен — валидный CSS-селектор
xpathТекст первого узла по XPath-выражению (можно и атрибут: //a/@href)обязателен — валидное XPath-выражение

Логика проверки одинакова для всех режимов: извлекаем значение → сравниваем со снимком предыдущей проверки → если отличается, отправляем alert и запоминаем новое значение. Первая проверка просто запоминает текущее значение и алерт не шлёт. Пробелы в тексте элементов (css/xpath) схлопываются, так что переносы строк в вёрстке ложных изменений не дают.

Если значение извлечь не удалось (селектор ничего не нашёл, элемент пустой, JSONPath отсутствует, regex не совпал) — это не «изменение», а ошибка проверки: монитор уходит в down (см. Уведомления).

hash реагирует на любой изменившийся байт — включая счётчики, токены и время в разметке. Для «живых» страниц надёжнее следить за конкретным фрагментом через css/xpath.

Вместо одиночного mode+selector можно задать массив правил (rules) — до 20 штук на один монитор. Каждое правило — это { "mode", "selector", "label" }, где label — необязательное человекочитаемое имя (оно же попадает в текст алерта). Alert срабатывает, если изменилось хотя бы одно правило. Это удобно, когда на одной странице нужно следить сразу за несколькими значениями (например, цена + наличие + рейтинг).

"rules": [
{ "mode": "css", "selector": ".product-price", "label": "Цена" },
{ "mode": "css", "selector": ".availability", "label": "Наличие" },
{ "mode": "text", "selector": "Скидка\\s+\\d+%", "label": "Скидка" }
]

Если rules задан, поля верхнего уровня mode/selector игнорируются. Если rules пуст — работает одиночный режим (mode+selector), который остаётся для обратной совместимости.

Снимок хранится по каждому правилу отдельно: перестановка правил ложных алертов не вызывает, а новое или изменённое правило на первой проверке только запоминает своё значение.

По умолчанию страница загружается обычным HTTP-GET. Если нужное значение подгружается скриптами уже в браузере (SPA, ленивая подгрузка цены и т.п.), включите флаг useBrowser: true — тогда страница рендерится в headless Chromium, и значения извлекаются из готового DOM после выполнения JS (text — по видимому тексту страницы, hash — по отрендеренному HTML).

Если проверка в браузерном режиме упала, Notifly снимает скриншот страницы (когда на сервере настроено хранилище скриншотов) и прикладывает его к алерту об ошибке: ключ приходит в extras сообщения под именем notifly::screenshot, картинка отдаётся по GET /browser-shot/<key> и показывается превью в карточке сообщения в админке. Алерты об изменениях по умолчанию уходят без скриншота. Кадры, не попавшие в алерт, не хранятся; доступ — только у владельца монитора; хранятся в пределах квоты хранилища тарифа и удаляются вместе с монитором.

СобытиеКогдаТекст
ИзменениеЗначение хотя бы одного правила отличается от снимка предыдущей проверки — при каждом измененииalertMessage + список изменений «было → стало» (значения усечены до 100 символов)
Ошибка проверкиСтраница не загрузилась (сеть, таймаут, HTTP-код ≥ 400) или правило не извлекло значение; один раз на падениеОшибка проверки: <причина>
Проверка восстановленаПервая успешная проверка после down — только если об ошибке был отправлен alert✅ Проверка восстановлена + какая была ошибка

Все три уходят с заголовком alertTitle и приоритетом alertPriority. Текст изменения собирается в Markdown, поэтому включайте alertMessageMarkdown: true (админка делает это сама). Пример алерта:

Цена товара #42 изменилась!
🔔 Обнаружено изменение
**Цена**
было: ~~1 299 ₽~~
стало: **1 199 ₽**

Если у правила нет label, в тексте будет «правило N».

Статусы монитора: pending (создан или возобновлён), up, down (последняя проверка — ошибка) и paused. Порога «N неудач подряд» у контент-монитора нет: в down уводит первая же ошибка.

  • Без спама. Об ошибке монитор сообщает один раз: момент алерта хранится в alertedAt, и пока монитор в down, повторных сообщений нет. Re-arm: если через PUT /content-monitor/:id изменить параметры проверки (URL, режим, селектор, правила, useBrowser, период, таймаут), отметка сбрасывается и проверка запускается сразу — если ошибка сохраняется, придёт свежий alert.
  • Таймаут сбрасывает снимок. После ошибки-таймаута первая успешная проверка запоминает значения заново и об изменении не сообщает. При остальных ошибках снимок сохраняется, и изменение, случившееся за время сбоя, будет замечено.
  • Сбои инфраструктуры Notifly (например, недоступен браузерный сервис) — не вина сайта: алерта нет, статус не меняется, квота возвращается, проверка повторяется через 2 минуты.
  • Окна обслуживания: в окне проверки идут и пишутся в историю, но алерт об ошибке откладывается до конца окна, а найденные изменения не сообщаются и не принимаются за новый снимок — после окна страница сравнивается со снимком до работ. Изменение, пережившее окно, будет замечено; временная заглушка «ведутся работы» — нет.
  • Тихие часы подавляют эскалацию во внешние каналы для сообщений с приоритетом ниже breakthroughPriority (по умолчанию 8).
  • Статус-страница канала: переход в down (ошибка проверки, не изменение) открывает авто-инцидент, восстановление — закрывает.
  • Каждая проверка — одно событие дневной квоты; при исчерпании проверки откладываются до следующих суток.

Прежде чем создавать монитор, удобно подобрать и проверить правило «на лету» — для этого есть два эндпоинта, не требующие создания монитора. Оба загружают страницу так же, как живой монитор, и применяют правило тем же кодом. Загруженная страница кэшируется на 90 секунд: повторные тесты по тому же URL её не перезагружают.

Загружает страницу и применяет одно правило, возвращая извлечённое значение или понятную ошибку:

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor/test-rule" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"url": "https://example.com/product/42",
"useBrowser": false,
"mode": "css",
"selector": ".product-price"
}'
# → {"ok":true,"value":"1 299 ₽"}

Ответ (всегда 200): ok (булево), value (извлечённое значение, усечено до 200 символов; для hash — короткий отпечаток страницы), error (текст ошибки, если страница не загрузилась или правило не сработало) и note (диагностика, например о деградации браузерного режима до GET).

Подобрать селектор нейросетью — POST /content-monitor/suggest-selector

Заголовок раздела «Подобрать селектор нейросетью — POST /content-monitor/suggest-selector»

Notifly загружает страницу, и нейросеть по вашему описанию определяет, что отслеживать, и находит текущее значение на странице; по этому значению сервер сам строит устойчивый CSS/XPath-селектор, а если не вышло — нейросеть синтезирует селектор по фрагменту страницы (с проверкой и повторами). Для JSON-ответа подбирается режим json. Результат всегда проверяется на реальном содержимом страницы, поэтому sampleValue в ответе — настоящее текущее значение:

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor/suggest-selector" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"url": "https://example.com/product/42",
"useBrowser": false,
"prompt": "следить за ценой товара"
}'

В ответе: mode, selector, label, sampleValue (текущее значение на странице), reason (почему так), confidence (high/medium/low, если есть оценка), alternatives (другие кандидаты с их sampleValue), fallback (true, если селектор подобран эвристикой — заголовок h1/title или хэш страницы — без нейросети или после её неудачи) и note. Поле prompt — до 500 символов. Запрос вида «следить за всей страницей» сразу вернёт режим hash без обращения к нейросети. Если страница не загрузилась, ответ — 502.

  1. Откройте app.notifly.ru → Мониторы → Создать → «Контент страницы изменился» (или вкладка Контент → «Добавить монитор контента»).
  2. Укажите URL, при необходимости включите «Браузерный режим». В блоке «Подбор селектора ИИ» опишите словами, за чем следить, нажмите «Сгенерировать селектор» и «Добавить в правила» — или задайте правила вручную.
  3. Кнопкой «Проверить» у правила убедитесь, что извлекается ровно то значение, которое нужно.
  4. Задайте период, таймаут, канал и текст алерта (поддерживается Markdown) — и сохраните.

Заголовок и приоритет алерта форма не настраивает (приоритет 5) — задайте их через REST API или MCP. Учтите, что сохранение монитора из формы вернёт приоритет к 5.

Одиночный режим (следим за одним CSS-значением):

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"appid": 12345,
"name": "Цена товара #42",
"url": "https://example.com/product/42",
"mode": "css",
"selector": ".product-price",
"useBrowser": false,
"intervalSec": 3600,
"timeoutSec": 15,
"alertMessage": "Цена товара #42 изменилась!",
"alertMessageMarkdown": true,
"alertPriority": 7
}'

Множественный режим (несколько правил на одной странице):

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"appid": 12345,
"name": "Карточка товара",
"url": "https://example.com/product/42",
"rules": [
{ "mode": "css", "selector": ".product-price", "label": "Цена" },
{ "mode": "css", "selector": ".availability", "label": "Наличие" }
],
"intervalSec": 3600,
"alertMessage": "Изменилось содержимое карточки товара.",
"alertMessageMarkdown": true,
"alertPriority": 5
}'

Мониторинг поля в JSON API:

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"appid": 12345,
"name": "Курс из API",
"url": "https://api.example.com/rates",
"mode": "json",
"selector": "$.usd.value",
"intervalSec": 600,
"alertMessage": "Курс USD изменился.",
"alertMessageMarkdown": true
}'
ПолеТипОбязательноОписание / ограничения
appiduintдаID канала, куда слать alert
namestringдаНазвание монитора, 1–200 символов
urlstringдаВалидный http(s)-URL на публичный адрес
modestringда*Одиночный режим: hash / json / text / css / xpath
selectorstringзависитJSONPath для json, regex для text, CSS для css, XPath для xpath; для json/css/xpath обязателен
rulesarrayда*Массив правил {mode, selector, label}, до 20; альтернатива mode+selector
useBrowserboolнетtrue — рендеринг через headless Chromium
intervalSecuintдаПериод проверки, 60–86400 секунд
timeoutSecuintнетТаймаут загрузки, по умолчанию 15, максимум 60
alertMessagestringдаТекст уведомления об изменении, 1–2000 символов
alertTitlestringнетЗаголовок всех уведомлений монитора
alertMessageMarkdownboolнетОтображать уведомления как Markdown (рекомендуется true)
alertPriorityintнетПриоритет уведомлений, 0–10

* Нужно задать либо rules (множественный режим), либо mode+selector (одиночный). Если rules непуст — mode/selector игнорируются. CSS-селекторы, XPath-выражения и regex проверяются на синтаксис при сохранении (400 при ошибке).

Новый монитор создаётся со статусом pending и проверяется почти сразу (nextCheckAt = «сейчас»). После изменения параметров проверки (URL, режим, селектор, правила, useBrowser, период, таймаут) проверка перепланируется на «сейчас»; правка названия, заголовка/текста alert и приоритета расписание не сдвигает. PUT /content-monitor/:id принимает тот же набор полей целиком (кроме appid).

  • POST /content-monitor/:id/pause — приостановить (проверки не выполняются; nextCheckAt отодвигается далеко в будущее).
  • POST /content-monitor/:id/resume — возобновить (статус снова pending, проверка планируется на «сейчас»).

В записи монитора хранятся lastValue (снимок последней успешной проверки — JSON-объект со значениями правил), lastError (последняя ошибка загрузки/извлечения), lastCheckAt, nextCheckAt и alertedAt. История проверок с извлечёнными значениями и найденными изменениями — GET /monitor-history/content_monitor/:id (см. Активные мониторы) и вкладка «История» в окне редактирования монитора.

  • Период проверки: 60–86400 секунд (от 1 минуты до 24 часов).
  • Таймаут загрузки: по умолчанию 15 с, максимум 60 с.
  • До 20 правил на один монитор.
  • Приоритет alert: 0–10; длина name — 1–200, alertMessage — 1–2000 символов.
  • Страница загружается GET-запросом без своих заголовков и тела; читается до 1 МБ ответа. HTTP-код ≥ 400 — ошибка проверки.
  • Запросы идут из облака: URL должен быть доступен из публичного интернета, адреса приватных сетей, loopback и link-local отвергаются.
  • Браузерный режим (useBrowser) работает, только если на сервере настроен браузерный сервис.

Авторизация — заголовок X-Notifly-Key с API-токеном (или токеном устройства). Создание, изменение, удаление, пауза и возобновление требуют у API-токена доступа write; для списка, test-rule и suggest-selector достаточно read. Токен, ограниченный каналами, видит и меняет только мониторы этих каналов. Share-токены каналов к управлению мониторами не допускаются (403).

МетодПутьОписание
GET/content-monitorСписок контент-мониторов пользователя
POST/content-monitorСоздать монитор
POST/content-monitor/suggest-selectorПодобрать режим + селектор нейросетью по описанию
POST/content-monitor/test-ruleПрименить одно правило к странице (dry-run), вернуть значение или ошибку
PUT/content-monitor/:idОбновить настройки (нельзя сменить appid)
DELETE/content-monitor/:idУдалить монитор (вместе со скриншотами)
POST/content-monitor/:id/pauseПриостановить проверки
POST/content-monitor/:id/resumeВозобновить проверки
GET/browser-shot/<key>Скриншот из extras алерта (только владельцу монитора)

Через MCP контент-мониторы тоже можно создавать и проверять правила словами — например, «следи за ценой на странице https://example.com/product/42 раз в час».

См. также: Активные мониторы, HTTP-мониторы, Workflow-мониторы, Browser-workflow.