changedetection.io следит за конкурентами ровно до первого JS-рендера
Поставили changedetection.io, чтобы отслеживать цены у поставщика или ассортимент конкурента, — и первую неделю всё отлично: страница изменилась, пришло уведомление. А потом на сайте, который вы больше всего хотели мониторить, инструмент замолкает намертво, хотя вы точно видите глазами, что цена там поменялась. Разбираемся, почему так происходит и что доустановить, чтобы changedetection.io видел не только HTML, который приходит на первый запрос, но и то, что дорисовывает JavaScript.
Содержание
- Что вообще делает changedetection.io и зачем он вам
- Установка: минимальный docker-compose для старта
- Из коробки: обычный HTTP-запрос и его честные пределы
- Где именно это ломается: SPA, отложенная подгрузка, инфинит-скролл
- Донастройка: подключаем Playwright/headless Chrome
- Точная настройка отслеживания: селекторы, фильтры, «шум»
- Уведомления и грабли эксплуатации
Что вообще делает changedetection.io и зачем он вам
changedetection.io — это self-hosted инструмент, который периодически забирает содержимое страницы, сравнивает с предыдущей версией и сообщает, если что-то изменилось. По задаче это не то же самое, что классический uptime-мониторинг: там важен факт «сайт отвечает / не отвечает», здесь важен факт «содержимое стало другим». Типичные сценарии:
- отслеживание цены конкретного товара у поставщика или конкурента;
- мониторинг страницы «в наличии» / «под заказ» у поставщика с ограниченным складом;
- слежение за условиями API/тарифов у стороннего сервиса, от которого вы зависите;
- контроль изменений на юридически значимых страницах (условия использования, политика возврата);
- отслеживание вакансий, тендеров, новостей на сайтах без RSS.
Инструмент бесплатный и open source, ставится в один Docker-контейнер, хранит историю версий страницы и умеет слать уведомления через десятки каналов (Telegram, email, Discord, вебхуки — через встроенный Apprise). Проблема, с которой сталкивается почти каждый, кто мониторит не блог на статическом HTML, а современный магазин или SaaS-лендинг: страница физически не отдаёт нужный текст в первом ответе сервера — она отдаёт пустой <div id="root"></div>, а содержимое дорисовывает React или Vue уже в браузере.
Установка: минимальный docker-compose для старта
Официальный образ публикуется на GitHub Container Registry, разворачивается одним сервисом:
version: "3.8"
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- ./datastore:/datastore
environment:
- BASE_URL=https://watch.example.com
Поднимаете (docker compose up -d), открываете http://ваш-ip:5000, добавляете первый watch — просто вставляете URL. По умолчанию проверка идёт раз в несколько минут (интервал настраивается глобально и на уровне отдельного watch), результат сравнения — построчный diff, который можно посмотреть в UI и подписаться на уведомления при изменении.
Для реального использования сразу закройте порт 5000 наружу и поставьте перед сервисом обратный прокси с TLS и базовой авторизацией — панель без пароля, торчащая в интернет, это открытая дверь к вашей карте отслеживаемых цен. Если ещё не разворачивали Compose-стек для прод-сценария с лимитами и healthcheck, есть отдельный разбор настройки Docker Compose для продакшена на VPS — те же принципы применимы и здесь.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSИз коробки: обычный HTTP-запрос и его честные пределы
Дефолтный метод получения страницы в changedetection.io — «Basic fast Plaintext/HTTP Client»: инструмент делает обычный HTTP-запрос (по сути как curl), получает HTML, который отдал сервер, и сравнивает его текстовое содержимое между проверками. Для огромного числа сайтов этого достаточно и даже избыточно хорошо:
- статические лендинги и блоги на серверном рендеринге (WordPress, Hugo, большинство корпоративных сайтов);
- страницы, где контент генерируется на бэкенде и приходит в HTML сразу (Django/Rails/PHP-шаблоны, серверный Next.js с полноценным SSR под нужным URL);
- документация, changelog-страницы, RSS/JSON API поставщика.
Это быстрый и дешёвый по ресурсам режим: один процесс, доли секунды на запрос, никакого браузера в памяти. Если ваш случай — именно такой сайт, дальше можно не читать про Playwright, разве что заглянуть в раздел про фильтры и уведомления.
Проблема начинается там, где страница на клиенте перерисовывается JavaScript-фреймворком. Откройте «Просмотр кода страницы» (не DevTools, а именно исходный HTML, который реально пришёл с сервера) на сайте на React или Vue без SSR — там часто нет ни цены, ни названия товара, только пустой контейнер и ссылки на бандлы JS. Простой HTTP-клиент видит ровно это: пустую обёртку. Цена появляется только после того, как браузер скачал и выполнил JS, обратился к API и вставил данные в DOM — то есть уже после первого рендера. changedetection.io без дополнительной настройки этот второй акт не видит вообще.
Где именно это ломается: SPA, отложенная подгрузка, инфинит-скролл
Симптом всегда один: watch стоит «зелёным», проверки идут по расписанию, но изменение цены он не замечает — либо наоборот, шлёт срабатывания на пустом месте, сравнивая случайный мусор в неотрендеренном HTML (например, хэш бандла в имени JS-файла, который меняется при каждом деплое фронтенда, хотя видимый контент не менялся).
Частные случаи, кроме «полностью пустого» SPA:
- Гидратация после API-запроса. Next.js/Nuxt в режиме SSR отдают начальный HTML с частью контента, но цена или остаток на складе подтягиваются отдельным клиентским запросом к API уже после загрузки — в исходном HTML этих данных просто нет.
- Ленивая подгрузка по скроллу. Каталог, где карточки товаров подгружаются по мере прокрутки (infinite scroll) — HTTP-клиент получает только первый экран, дальше ничего не «доскроллит».
- Контент за действием пользователя. Цена появляется только после выбора варианта товара, региона или после клика «показать цену» — простой fetch этого клика не сделает.
- Антибот-проверки. Часть SPA-сайтов заодно ставит JS-челлендж (Cloudflare, аналоги) — обычный HTTP-клиент получает страницу-заглушку с challenge вместо контента вообще для любого сайта, не только SPA.
Первые три случая лечит headless-браузер с полноценным рендерингом. Четвёртый снимается лишь частично: браузер честно проходит простой JS-челлендж, но со сложной антибот-защитой стабильности не обещает никто — если сайт целенаправленно блокирует автоматических посетителей, changedetection.io не волшебная палочка.
Донастройка: подключаем Playwright/headless Chrome
В changedetection.io для каждого watch можно выбрать метод получения страницы, и кроме быстрого HTTP-клиента там есть режим рендеринга через реальный браузер (Chromium через Playwright). Разница в том, что вместо одного HTTP-запроса инструмент открывает страницу в headless Chrome, ждёт выполнения JS и уже с готового, отрендеренного DOM берёт содержимое для сравнения.
Из коробки в главном образе браузер не поднят — это осознанное разделение: держать Chromium в том же контейнере, где крутится Flask-приложение, неудобно с точки зрения ресурсов и обновлений. Практическая схема — второй контейнер с headless-браузером, к которому changedetection.io подключается по сети:
version: "3.8"
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- ./datastore:/datastore
environment:
- BASE_URL=https://watch.example.com
- PLAYWRIGHT_DRIVER_URL=ws://browser-chrome:3000
depends_on:
- browser-chrome
browser-chrome:
image: dgtlmoon/sockpuppetbrowser:latest
container_name: browser-chrome
restart: unless-stopped
cap_add:
- SYS_ADMIN
environment:
- SCREEN_WIDTH=1920
- SCREEN_HEIGHT=1080
Точное имя переменной окружения и образа браузерного компаньона может отличаться между версиями changedetection.io — перед разворачиванием сверьтесь с docker-compose.yml в актуальной официальной документации, но принцип неизменен: отдельный контейнер с headless Chrome, доступный по WebSocket, и переменная в основном сервисе, которая на него указывает.
После того как соединение с браузером настроено, в UI при добавлении watch появляется выбор метода: «Basic fast Plaintext/HTTP Client» (по умолчанию) и вариант с рендерингом через Chromium/Playwright. Переключаете конкретный проблемный watch на браузерный режим — остальные, для которых хватает обычного HTTP, оставляете как есть, не нагружая браузер лишней работой.
Важный практический момент по ресурсам: headless Chrome — это не лёгкий процесс. Каждая параллельная проверка с рендерингом — это открытая вкладка с полноценным движком рендеринга, счёт идёт на сотни мегабайт оперативной памяти на инстанс, а если проверок с браузерным методом много и они идут одновременно, сервер ощутимо нагружается по CPU и RAM. Если у вас уже был печальный опыт с headless-браузером, который незаметно съедал место на диске временными профилями, — это отдельная и довольно частая грабля, разобрана подробно здесь: та же логика применима и к браузерному контейнеру changedetection.io, если его не перезапускать регулярно.
Точная настройка отслеживания: селекторы, фильтры, «шум»
Включить рендеринг браузером — только половина дела. Вторая половина — сузить то, что именно сравнивается, иначе получите либо тишину, либо спам ложных срабатываний.
CSS/XPath-селекторы. В настройках watch есть поле «Include Filters», где можно указать CSS-селектор конкретного блока — например, только элемент с ценой (.product-price или аналогичный класс на конкретном сайте), а не всю страницу целиком. Это одновременно решает две проблемы: инструмент сравнивает только нужный фрагмент DOM (который уже отрендерен браузером) и не реагирует на изменения в соседних блоках — счётчике просмотров, каруселях, рекомендациях «вам может понравиться», дате в подвале.
Игнорирование «шумных» частей. Отдельное поле для правил, которые нужно вырезать из сравнения даже внутри выбранного блока — например, регулярным выражением убрать динамический таймер обратного отсчёта акции или постоянно меняющийся счётчик «осталось N штук», если вас интересует именно цена, а не эти цифры.
Триггер на текст, а не просто «diff». Можно настроить срабатывание не на любое изменение, а на появление/исчезновение конкретной фразы («Нет в наличии», «В корзину») — полезно, когда важен не факт правки текста, а конкретное бинарное состояние.
Интервал проверки. Частые проверки — быстрее реакция, но больше нагрузка и выше шанс попасть под rate limit или блокировку по IP. Для мониторинга цен конкурента раз в 15–30 минут обычно достаточный компромисс; для критичных по времени акций интервал можно сократить точечно для одного watch, не трогая остальные.
Таблица — какой режим для какой задачи:
| Сценарий | Метод получения | Доп. настройка |
|---|---|---|
| Блог, статический лендинг, серверный HTML | Basic HTTP Client | обычно не нужна |
| Каталог на React/Vue без SSR | Playwright/Chromium | Include Filter на блок цены |
| Товар с выбором варианта перед показом цены | Playwright/Chromium | сценарий клика (если поддерживается) или мониторинг API-эндпоинта напрямую |
| Список с infinite scroll | Playwright/Chromium | ожидание подгрузки + селектор на нужную карточку |
| Сайт с JS-антибот-челленджем | Playwright/Chromium | не гарантирован стабильный результат |
Уведомления и грабли эксплуатации
Уведомления настраиваются через встроенный Apprise — единый URL-формат, который поддерживает Telegram, Discord, email, Slack, generic-вебхук и ещё десятки сервисов. Для Telegram это обычно строка вида tgram://<bot_token>/<chat_id>, добавляется в настройках watch или глобально для всех. Прежде чем полагаться на changedetection.io как на единственный канал оповещений, стоит помнить общий принцип: мониторинг, который никто не смотрит, — это не мониторинг — заведите канал, который вы реально проверяете, а не тихую вкладку в браузере.
Грабли, с которыми реально сталкиваются на практике:
- Rate limit и бан по IP. Мониторите десятки страниц одного сайта с частым интервалом — легко попасть под защиту от ботов: сайт начинает отдавать капчу или 403 всем запросам с вашего сервера, включая обычные HTTP-проверки других watch. Решение — реже проверять, разносить интервалы, при необходимости идти через прокси.
- Ложные срабатывания от рекламы и виджетов. Баннеры, счётчики онлайн-консультанта, случайные ID сессии в разметке — если не сузить сравнение фильтром, уведомления посыплются на пустом месте уже в первый день.
- Память браузерного контейнера растёт со временем. Chromium под нагрузкой периодически стоит перезапускать (
restart: unless-stoppedплюс расписание пересоздания контейнера, а не бесконечная работа одного процесса) — иначе накопленные вкладки и кэш постепенно съедают RAM. - changedetection.io не подскажет, почему сайт лежит. Это инструмент про содержимое, а не про доступность — если нужен отдельный контроль «жив ли сайт вообще», это соседняя задача, для которой в связке удобнее что-то вроде Uptime Kuma — changedetection.io его не заменяет и не должен.
- HTML-структура сайта меняется при редизайне. Если конкурент обновил вёрстку, CSS-селектор может перестать находить элемент — watch не упадёт с заметной ошибкой, просто перестанет ловить изменения. Перепроверяйте фильтры вручную, особенно если замечаете долгое затишье по конкретному watch.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужен ли Playwright для всех watch, или можно включить выборочно?
Выборочно, и это правильный подход — метод получения страницы выбирается для каждого watch отдельно, браузерный режим включайте только там, где реально нужен рендеринг JS, чтобы не грузить сервер лишним Chromium.
changedetection.io умеет логиниться на сайт перед проверкой?
В браузерном режиме есть возможность задать шаги перед снятием снимка (клики, ввод текста), но для сайтов за полноценной авторизацией это усложняет и удлиняет каждую проверку — проверяйте актуальные возможности конкретной версии, прежде чем строить на этом мониторинг.
Почему changedetection.io не видит изменение, хотя я включил Playwright?
Чаще всего причина не в браузере, а в селекторе фильтра — либо он указывает не на тот блок после обновления вёрстки сайта, либо контент подгружается позже таймаута ожидания рендеринга. Проверьте вручную через «Просмотр снимка» в UI, что именно инструмент видит после рендеринга.
Можно ли мониторить API вместо страницы, если знаю эндпоинт?
Да, и часто это надёжнее — если открыть DevTools → Network на сайте и найти JSON-запрос, который отдаёт цену или остаток напрямую, можно указать этот URL как watch с обычным HTTP-методом: он и быстрее, и не требует браузера, и не ломается при редизайне фронтенда.
Сколько ресурсов закладывать на VPS под changedetection.io с браузером?
Точная цифра зависит от числа watch с включённым рендерингом и частоты проверок, но headless Chrome — заметно более прожорливый сосед, чем сам changedetection.io: закладывайте отдельный запас RAM под браузерный контейнер и не экономьте на нём, если планируете десятки SPA-сайтов в мониторинге одновременно.
Что делать, если сайт вообще блокирует автоматических посетителей антибот-защитой?
Технически это уже не задача рендеринга JS, а обход детектирования — headless-браузер не гарантирует прохождение таких проверок стабильно, и если это единственный способ получить данные, стоит трезво оценить, стоит ли овчинка выделки, или искать открытый API/RSS того же поставщика.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →