← Все системы Кейс 03 · AI-система

Цена на маркетплейсе меняется без вас

Уход своей цены ниже согласованного прайса и демпинг соседа по полке видно в тот же день, а не в конце месяца по отчёту — сразу на Wildberries и на Ozon. Человека система зовёт только тогда, когда есть что решать.

2маркетплейса под одной сверкой
50позиций прайса под ежедневной сверкой
28групп сравнения с конкурентами
1 000+карточек конкурентов в опросе
6замеров в сутки на каждой площадке
0ручных сверок в день
Задача

Цену видно, а истории цены нет

Продавец живёт сразу в двух ценах: своя обязана держаться РРЦ, согласованного с брендом, а цена соседа по полке решает, купят у вас или у него. Обе меняются в течение дня — акции, автоскидки, кошелёк.

Ошибка стоит дорого в обе стороны. Ушли ниже РРЦ — вопросы от бренда, а в пределе условия поставки. Ушли выше рынка — заказы уходят соседу, и понимаешь это через месяц по отчёту. Переписать цифры в блокнот можно, но через час они другие, а вчерашней цены конкурента не найти нигде.

Площадок при этом две, и они не копия друг друга: свой прайс, своя витрина, свой набор соседей по полке. Вывод, снятый на одной, на второй может быть прямо противоположным — а сверять руками приходится обе.

  • Ушли ниже РРЦ — вопросы от бренда
  • Ушли выше рынка — заказы у соседа
  • Тихий демпинг съедает маржу незаметно
  • Вчерашней цены конкурента не найти
  • Сравнение без объёма даёт ложный вывод
  • Две площадки — двойной ручной обход
Что построено

Одна страница на оба маркетплейса

Дашборд мониторинга цен: вкладки по категориям, показатели по прайсу и сетка карточек сверки с РРЦ
ДашбордСколько позиций ушло ниже и выше прайса, сколько добрать к новому, где конкурент дешевле. Экран отвечает на «чем заняться сегодня», а не «какие есть данные».
Тот же дашборд с выбранной вкладкой Ozon: показатели по прайсу и начало блока сравнения с конкурентами
Вторая площадка — соседняя вкладкаПереключатель наверху — не фильтр, а другой маркетплейс со своим прайсом, своими группами и своими конкурентами. Цифры расходятся сильно: на Wildberries ниже прайса 24 позиции из 25, на Ozon — четыре, а семнадцать держат РРЦ в пределах процента.
Сетка карточек: по каждой позиции прайса цена, отклонение от РРЦ и график за период
Своя цена против прайсаКарточка на позицию: цена, отклонение от РРЦ в рублях и процентах, график за период. Фильтр по отклонению собирает список позиций, которые надо подтянуть первыми.
Крупный график одной позиции: линия цены на витрине и горизонталь действующего РРЦ
Крупно по одной позицииСиняя — цена на витрине, серая — действующий РРЦ. Больше недели позиция простояла почти вдвое дешевле прайса, потом дважды улетала выше него и сейчас держится чуть ниже: 1 134 ₽ при РРЦ 1 250. Такую историю витрина не помнит — она показывает только сегодняшнюю цену.
Конкурентное сравнение: две группы сопоставимых товаров, линия на каждый SKU и расшифровка под графиком
Полка целикомСравниваются только товары одного типа и объёма — иначе литровая бутыль «выигрывает» у стограммовой. Вывод получается не «мы дороже вообще», а «мы четвёртые в этой группе».
Крупный график группы: девять линий конкурентов, пунктиром РРЦ наших позиций и таблица с ценой за 100 единиц
Крупно по группеК линиям добавляется пунктир РРЦ наших позиций. Отсюда видно главное: есть ли куда снижаться в ответ на демпинг, не нарушив договорённость с брендом. Розовая птичка с ценой сверху — товар, который на неделю уехал в 5 692 ₽: шкала обрывается перед ним, иначе он сплющил бы всю группу в полосу у нижнего края.
Крупный график группы на Ozon: десять линий продавцов, пунктиром РРЦ наших позиций и таблица с ценой за 100 мл
Та же группа на OzonМеханика сравнения одна, состав полки другой. Три продавца стоят дешевле нас, Harly — заметно дороже: 668-756 ₽ за 100 мл против наших 420-582. На Wildberries эта же группа собирается из других соседей: часть здешних там не торгует, а часть прямых конкурентов WB на Ozon ушла в другую категорию. Поэтому «мы дороже рынка» — утверждение, верное только внутри одной площадки.
Табличный вид сверки: позиции прайса с ценой, РРЦ, отклонением, статусом и суммой добора к новому прайсу
То же таблицейОтклонение, статус «ниже / держим / выше» и сумма добора по каждой позиции — готовый план подъёма цен к переходу на новый прайс.
Главное решение

Замер идёт сам, а человека зовут только на изменение

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

Под этим — разделение, ради которого систему стоило собирать инженерно, а не «в таблице». Сбор вынесен в отдельные сервисы на Python: Wildberries блокирует Node по TLS-отпечатку и отдаёт 403 там, где Python получает 200. Дашборд не ждёт сценарий и опрашивает сборщики сам — поэтому история копится, даже когда сценарий упал или его переписывают.

Ровно это разделение и позволило добавить Ozon, не трогая ничего выше. Ozon серверу витрину не отдаёт вовсе: с Railway приходит 403 от антибота, а через официальный Seller API видно только собственные товары — конкурентов там нет и не будет. Поэтому второй сборщик устроен иначе: свои цены он берёт по ключам API, а полку конкурентов снимает живой Chrome на рабочей машине, шесть раз в сутки по расписанию. Наружу оба сборщика отдают одинаковые строки, так что ни сценарии, ни дашборд про эту разницу не знают.

Как это выглядит у человека. Приходит сообщение: позиция, старая и новая цена, отклонение. Дальше решение на месте или клик в дашборд — посмотреть, что творилось с позицией и её группой за неделю. Минуты вместо обхода карточек.

Одна сетка времени на всех. Замер в 11:03 подписывается как 11:00 — ровно той строкой, что уходит в таблицу. Иначе в разговоре с брендом пришлось бы выбирать, какой из двух историй верить.

Разный сбор, одинаковый ответ. Wildberries отвечает по HTTP, Ozon — только живому браузеру. Разница заканчивается внутри сборщика: наружу оба выдают один формат строки, и вторая площадка не потребовала ни второй таблицы формул, ни второй версии дашборда.

Архитектура

Как это устроено

ДВЕ ПЛОЩАДКИ ДВА СБОРЩИКА · PYTHON ДВА ПОТРЕБИТЕЛЯ · ОДНА СЕТКА ЧАСОВ ЧТО ВИДИТ ЧЕЛОВЕК Wildberries · 6 продавцов, 550+ SKU · цена, остаток, категория Node сюда не пускают Ozon · 5 продавцов, ~450 SKU · свои товары — Seller API · витрина закрыта антиботом: серверу отвечают 403 wb-collector · свой TLS проходит там, где Node получает 403 · цена с кошельком формулой ozon-collector · свои цены — по ключам API · полка — живой Chrome на машине, 6 раз в сутки по расписанию · CSV забирается из страницы наружу оба отдают одинаковую строку Сценарии n8n · каждые 4 часа · журнал всех замеров в таблицы · шаг цены и отклонение от средней · за порог вышло — собрать сводку Дашборд · те же часы + 3 мин · ходит к сборщикам сам · история в volume, переживает деплой · 50 позиций прайса, 28 групп · сценарий упал — история копится Две Google-книги журнал и витрины, WB и Ozon Телеграм только то, что сдвинулось Страница с графиками прайс против РРЦ, сравнение по группам обе точки подписаны меткой одного слота: 11:03 — это 11:00 PYTHON · N8N · GOOGLE SHEETS · TELEGRAM BOT · RAILWAY · DOCKER · CHROME CDP ЗАВИСИМОСТЕЙ НЕТ · СТАНДАРТНАЯ БИБЛИОТЕКА СБОР ОТДЕЛЬНО ОТ ПОКАЗА: УПАЛ ОДИН ПОТРЕБИТЕЛЬ — ВТОРОЙ ПРОДОЛЖАЕТ ПЛОЩАДКУ ЗНАЕТ ТОЛЬКО СБОРЩИК: ТРЕТИЙ МАРКЕТПЛЕЙС — ЭТО ЕЩЁ ОДИН СБОРЩИК, А НЕ НОВАЯ СИСТЕМА

Сборщики и дашборд написаны на чистом Python без внешних зависимостей, в Docker на Railway; обход витрины Ozon — единственная часть, которой нужна живая машина с браузером, и она вынесена ровно в эту одну коробку. Практический смысл: нет подписок, которые подорожают, и чужих библиотек, из-за которых через год всё переписывать.

Инженерные решения

Что отличает рабочий мониторинг от таблицы с ценами

01

Две динамики вместо одной

Шаг с прошлого замера ловит резкое движение и пропускает медленное: минус полтора процента за раз не превысит порога, а за неделю это минус двадцать. Вторая величина — отклонение от недельной средней — и ловит тихий демпинг.

02

Сравнивать можно только сопоставимое

Цена бутылки ни о чём не говорит, пока не известен объём. Товары разложены по группам и сравниваются по цене за 100 единиц — иначе решение принимается по ложному сравнению.

03

В базу пишется сырая цена

Скидки кошелька в данных площадки нет, она выводится формулой. В журнал пишется исходная цена: площадка поменяет условия — историю пересчитают, и двухлетний архив не обесценится.

04

Единицу измерения нельзя угадывать по названию

«Лососевое масло с астаксантином» не содержит слова «мл», и разбор текста давал «шт». Единица берётся из типа товара или из группы: одна такая ошибка переворачивает вывод, дешевле конкурент или дороже.

05

Первый прогон молчит

У нового продавца все товары формально «изменились»: сравнивать не с чем. Такие строки помечаются первичными, уведомления идут со второго замера — иначе подключение конкурента даёт сотню ложных сигналов, и канал перестают читать.

06

Сбой уведомления не роняет замер

Однажды из-за отказа при отправке не записались собранные данные — потерян был не алерт, а сам замер. Теперь доставка помечена как некритичная: цену в тот час уже не переснять, а сообщение можно послать позже.

07

Площадку, которая не пускает сервер, снимает живой браузер

Ozon отвечает облаку 403 от антибота, а в Seller API конкурентов нет вовсе — только свои товары. Поэтому витрину снимает Chrome со своим профилем на рабочей машине: расписание поднимает его само, проходит проверку, забирает готовый CSV прямо из страницы и гасит окно. Прятаться в headless бессмысленно — на него антибот реагирует злее, чем на обычное окно.

08

Один обход, два приёмника разной ширины

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

09

Источник правды по цене только один

Прайс обновили — и обновили в одном месте, на позициях Wildberries; на Ozon семь цен из шестнадцати месяц прожили старыми, а сверка при этом выглядела абсолютно рабочей. Теперь перенос односторонний и делается скриптом, который цен не хранит вовсе: берёт РРЦ у позиции WB и кладёт парной позиции Ozon. Пары выписаны руками — по названию их не подобрать, на второй площадке карточки зовутся иначе.

10

Один выброс не должен схлопывать весь график

Товар, который на неделю уехал в 4 500-5 700 ₽ при рынке 260-1 440, прижимал всю группу полосой к нижнему краю — график переставал отвечать на вопрос, ради которого нарисован. Шкала теперь ищет в ценах разрыв и обрывается на нём, а то, что за краем, помечено птичкой и подписано в подсказке. Порог разрыва не выбран, а подобран прогоном по всем группам: при более мягком под обрезку попадали живые группы с настоящим разбросом цен.

11

Дата в таблице считается по часовому поясу книги

Формула «с первого сентября берём новый прайс» первого сентября отдавала старый: в поясе книги было ещё тридцать первое августа. Сверка показывала отклонение +95%, и цифра выглядела совершенно настоящей — такие ошибки не падают, а тихо врут. Зависимость витрин от текущей даты убрана совсем.

Результат

Что изменилось

Было

Сверяли руками, когда доходили руки, и по каждой площадке отдельно. Расхождение с прайсом замечали постфактум, вчерашней цены конкурента было не найти, а на вопрос «почему просели заказы» отвечали догадками.

Стало

Расхождение с прайсом правят в день, когда оно возникло, на обеих площадках сразу. Чужое снижение цены видно не по итогам месяца, а в тот же день, и ежедневный обход карточек ушёл из работы менеджера совсем.

Для бизнеса

4 часа — столько теперь живёт нарушение прайса или чужой демпинг, прежде чем о нём узнают. Раньше это были недели. Плюс своя история цен рынка, которую задним числом не восстановить ничем.

Вторая площадка дала не удвоение отчёта, а вывод, которого раньше не было. Оказалось, что конкуренты на маркетплейсах разные: продавец, который на Wildberries бьётся с нами по маслам, на Ozon торгует в основном косметикой и в этой полке не участвует вовсе, зато по сахарозаменителям там плотно стоят те, кого на WB в группе нет. И собственный прайс площадки держат по-разному: на Wildberries ниже РРЦ ушли 24 позиции из 25, на Ozon — четыре.

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

Переносимость

Ту же схему ставят на что угодно

Площадку знает только сборщик; всё, что выше, работает с числами во времени. Это была гипотеза — Ozon её проверил: пороги, формулы витрин, вёрстка графиков и логика уведомлений остались нетронутыми, хотя сам способ добычи данных сменился с HTTP-запроса на живой браузер. Добавились ровно две вещи: второй сборщик и справочники второй площадки.

Поэтому третий маркетплейс, остатки, позиции в поиске или отзывы — это правка, а не новый проект: замер → журнал → пороги → человеку показывают только изменение.

Стек

На чём собрано

Сбор: Wildberries

  • Python, стандартная библиотека
  • HTTP-сервис на токене
  • Категории и типы товаров
  • Цена с кошельком формулой

Сбор: Ozon

  • Ozon Seller API — свои товары
  • Chrome + CDP — полка конкурентов
  • Планировщик, 6 запусков в сутки
  • Сторож против неполного замера

Логика

  • n8n, запуск каждые 4 часа
  • Сравнение с прошлым замером
  • Отклонение от недельной средней
  • Пороги и группировка сводки

Хранение и витрины

  • Google Sheets: две книги, журнал и срез
  • Сверка РРЦ и конкуренты
  • Дашборд на Python
  • История в volume, архив 2 года

Доставка и хостинг

  • Telegram Bot, темы по категориям
  • Railway, Docker
  • Вход по логину и паролю
  • Claude Code

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

Нужен такой мониторинг цен?

От вас — прайс, площадки и список конкурентов. Остальное на мне: сбор, история, пороги и уведомление, которое приходит само.