От Google таблиц к DataLens + PostgreSQL: как мы делали BI-систему для WB и Ozon

От Google таблиц к DataLens + PostgreSQL: как мы делали BI-систему для WB и Ozon - 1

Всем привет! Меня зовут Александр. Хочу рассказать, как мы прошли путь от Google таблиц до собственной BI-системы на PostgreSQL + DataLens с версионированием себестоимости, оптимизацией JOIN и автоматической выгрузкой данных из API WB и Ozon.

Как обычная потребность в нормальной аналитике личных кабинетов на маркетплейсах привела к созданию продукта — сервиса.

Я работаю в компании, которая ведет личные кабинеты на Wildberries и Ozon. Ведет давно и успешно. И мы активно пользовались разными сервисами аналитики. Их много. Постоянно появляются новые, бесконечные звонки от менеджеров МП… что-то там.

Мы пробовали разные варианты. Ну и Google таблицы. Куда без них, это база. Нет, я ничего не имею против них. Даже наоборот, я всеми руками за. Для некоторых задач они не имеют нормальной альтернативы.

И тут логически подступает какое-то “но”. Какая-то проблема. Боль. И да, их есть у меня.

В чем боль, где проблема?

Сервисов и источников информации много. Глаза разбегаются. Что использовать? Что лучше? Пытаешься собрать все это в кучу, как-то свести. В итоге тратишь время. Много времени.

А как же сервисы, ведь они умные, классные, крутые. Вон там сколько данных. А что толку? В них смотришь и не видишь общей картины. Так мы и не нашли вариант, который бы нас устраивал.

И в какой-то момент пазл сошелся так, что мы сами отлично разбираемся в аналитике маркетплейсов. Можем писать скрипты для выгрузки данных. Знакомы с API-протоколами WB и Ozon и знаем, как с ними работать. Есть опыт сайтостроения и понимание принципов, как это все работает. И к этому набору добавилось знакомство с сервисом Яндекс DataLens.

Вот тут-то все и сошлось. DataLens был тем недостающим звеном, которое поможет нам создать что-то реально крутое. Нашего опыта хватает, чтобы сделать свою аналитику. Так, как мы хотим.

Аналитике быть!

И мы стартанули. Начали планирование. Составлять схемы, планы: что хотим, что можем.

Уже в процессе планирования мы быстро поняли, что из этого может получиться что-то большее. Не только аналитика для себя, но и аналитика для других селлеров. Если уж мы такие привередливые и нам это полезно и понятно, значит, это будет интересно и другим. Стало еще интереснее.

Я с головой ушел в проект. Стал ответственным за это направление. Начал активно изучать DataLens на Яндекс Практикуме. Обучение там разбито на несколько блоков, понятно и доступно. И уже пройдя пару блоков, я срываюсь в практику и начинаю строить наш первый дашборд на имеющихся данных из Google таблиц (обучение все-таки закончил позже).

Туда мы загружали данные из API Ozon и WB. И вот уже первые графики и таблицы готовы. Мы получили первую версию дашборда. Назвали мы его: Концентрат

Смотрим, наблюдаем. Что-то не то. То Google глючит, то таблица отвалилась, то данные не грузятся. Понимаем, что нам это не подходит.

Приходим к тому, что нужно что-то большее. Нужен свой сервер и база данных. Нужно ускоряться.

Свой сервер

Погнали. Создаем свой виртуальный сервер на хостинге. Настраиваем. Пишем скрипты, которые выгружают данные по API и сохраняют все в базу данных MySQL (по инерции решили использовать ее, потом пришлось переделывать на PostgreSQL).

Тут было много манипуляций. Постоянные доработки, улучшения, оптимизация, которые продолжаются и сейчас. И вот получаем первую стабильную версию дашборда. Настраиваем автоматическую ежедневную выгрузку данных через Cron.

Создали дашборд так, как мы хотели. С нужными графиками, таблицами. Вот оно!

Настроили сквозные фильтры на дошборде. Без них никуда.

Настроили сквозные фильтры на дошборде. Без них никуда.

Работает. Все стабильно. Коллеги начинают пользоваться. Что-то уже начинает вырисовываться. Правим баги, продолжаем наблюдение.

Лирическое отступление про ИИ

Конечно было бы глупо не использовать во всех этих схемах ИИ модели. Ну до чего же шикарны ИИ от Anthropic. Claude Sonnet хорош, а Claude Opus просто лучший. Все остальное что мы использовали было мимо. Две большие разницы, как говорится.

Если ИИ делают все вместо тебя, не факт, что получится что-то годное. Но когда они выступают в роли помощников, тогда все получается.

Они серьезно сократили сроки разработки и сэкономили кучу времени.

Ядро

Еще на этапе планирования и составления дорожной карты мы хотели внедрить Архитектуру 2.0. В какой-то перспективе. Но перспектива эта наступила быстрее, чем мы думали. Практически сразу.

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

Файлы ядра запускается с конфигом клиента. Главная особенность: поправил ядро — и для всех клиентов обновилось. Изменился какой-то протокол — внесли изменения, и у всех обновления накатились.

Так выглядит ядро скриптов для WB.

Так выглядит ядро скриптов для WB.

Еще мы добавили переменные в скриптах. Например, в файле ядра есть тайминги, заданные по умолчанию. Если проект более-менее стандартный, эти тайминги отлично отрабатывают. Но если данных много, скрипты могут уходить в ошибку 429, а нам этого не нужно. Тогда с помощью таймингов можно скорректировать настройки для каждого клиента.

Посмотрели логи ежедневной выгрузки, если где-то есть ошибки — чуть подправили настройки, и все снова корректно.

Так, практически на старте, мы сделали дашборды по принципу ядра.

Начинаем масштабировать

Итак, мы создали свой дашборд. Сделали полноценную демо-версию на реальных данных с обезличенными артикулами. Проводим несколько встреч и созвонов со знакомыми селлерами. Показываем, что у нас есть, интересно ли им это. Получаем обратную связь.

Норм, интерес есть. Потенциал явно просматривается.

Теперь это нужно как-то масштабировать и заворачивать в полноценный продукт — сервис.

И тут начинается новый этап. Создаем страницу на сайте. Расписываем все подробно. Делаем ссылку на демо-версию. Описываем условия. Делаем триальную версию на 14 дней.

Подключаем первые боевые проекты. Понимаем, что вручную подключать не вариант. Неудобно. Начинаем продумывать, как автоматизировать процессы.

Коннектим форму на сайте с сервером через вебхук. Пишем скрипт — оркестратор по заведению нового клиента. Завелось!

Форма заявки на сайте

Форма заявки на сайте

Дальше. Для нормальной работы дашборда и его максимального функционала нужны данные от пользователя. Хотя бы себестоимость и данные по сумме плана. Их нужно где-то вводить. Делаем связку с Google таблицами. А что, удобно и привычно. Настраиваем скриптами выгрузку из таблиц. Работает.

Google таблица для заполнения себестоимости и Плана. Ссылки на обновление данных появляются после активации дашборда.

Google таблица для заполнения себестоимости и Плана. Ссылки на обновление данных появляются после активации дашборда.

Мы автоматизировали заведение нового клиента. Он заполняет форму, под него заводится новый дашборд. На почту приходят доступы. Круто.

Через минут 15 приходит первое письмо о том, что дашборд создался и оперативные данные выгрузились. Еще через час-полтора приходит второе письмо с информацией о загрузке исторических данных:

  • на WB это примерно полгода — год;

  • на Ozon до 2 лет.

Ozon в этом плане дает бОльшую историю, чем WB.

Вот это уже серьезно. Это уже похоже на полноценный продукт. Дальше допиливаем мелочи, которые можно допиливать до бесконечности.

Оптимизация

Сначала мы создали наши дашборды на MySQL. Оно как-то по инерции получилось: “база данных = MySQL”. Но уже в процессе поняли, что это не лучшее решение, и лучше собрать на PostgreSQL. Переписали скрипты под PostgreSQL.

Далее мы увидели, что данных у нас немало, и дашборд какой-то задумчивый. Начали искать пути ускорения. Полезли в структуру данных. Поняли, что для ускорения нужно максимально снизить количество JOIN в датасетах DataLens. Все, что можно рассчитать на сервере, считаем там.

Например, если выгружаются данные по рекламе, нужна возможность фильтрации по категории. В исходной таблице нет столбца с названием категории товара, но он есть в таблице номенклатуры. Раньше мы подтягивали эти данные через JOIN в DataLens.

В итоге добавили необходимые столбцы в таблицы и подгрузили данные в них через скрипты обогащения. Скрипт рекламы выгружает свои данные, а потом запускает обогащение из другой таблицы по ключу, например vendor_code (артикул товара).

И так со всеми таблицами: если можно что-то догрузить, чтобы не делать связку — догружаем. База чуть увеличивается, зато дашборд работает быстрее.

Далее для расчета экономики, структуры затрат и итоговой прибыли дашборд считал данные “на лету”, в датасете. Это были тяжелые графики, которые долго грузились. Написали скрипты, которые считают эти данные на стороне сервера и сохраняют результат в БД. А DataLens уже выводит готовые значения.

30 секунд в день для работы скриптов на сервере — и график летает.

Графики с прибылью и структурой затрат. Данные считаются на стороне сервера.

Графики с прибылью и структурой затрат. Данные считаются на стороне сервера.

Так на дашборде JOIN встречаются всего пару раз. После оптимизации разница стала огромной: если взять один и тот же период, время загрузки сократилось с 15-20 секунд до примерно 5 секунд.

Текущий стек

┌──────────────────────────────┐
│ API WB / OZON                │
│ (JSON/REST)                  │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐        ┌──────────────────────────────┐
│ Python скрипты               │ <----> │ Google таблицы               │
│ (парсинг, нормализация)      │        │ (себестоимость, план)        │
└──────────────┬───────────────┘        └──────────────────────────────┘
               │
               ▼
┌──────────────────────────────┐
│ PostgreSQL 16                │
│ (основное хранилище)         │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ Яндекс DataLens              │
│ (визуализация)               │
└──────────────────────────────┘

Технологии:

  • Backend: Python, psycopg2, requests, subprocess, python-dotenv, Flask

  • БД: PostgreSQL 16, SQL

  • Автоматизация: Cron (ежедневно), оркестратор заявок

  • BI: Яндекс DataLens

Версионированная себестоимость

Отдельно хочется выделить кейс с себестоимостью. Себестоимость — цифра непостоянная. Например, с сентября она повысилась. Загружаем новые данные. И что дальше? А дальше она пересчитается задним числом, и получатся некорректные данные по прибыли.

А если нужно что-то пересчитать в предыдущем периоде? Например, если себестоимость была неверно установлена.

Тут нужно было что-то придумать: механизм изменения данных без поломки истории.

В итоге пришли к такому варианту: в таблицу, где пользователь указывает себестоимость, добавили столбец “дата, с которой учитывается эта себестоимость”.

Как это работает:

  • если дату не указывать, значение используется как базовое;

  • если себестоимость изменилась, добавляется новая строка с датой начала действия;

  • начиная с этой даты, продажи считаются уже по новой себестоимости;

  • можно проставлять себестоимость задним числом.

В итоге получаем версионирование себестоимости. Ничего не ломается. Данные не искажаются и заносятся в БД по ключу start_date + vendor_code. А скрипты уже знают, как с этим работать.

Пример данных:

INSERT INTO wb_cost_price VALUES
('ART-001', 800.00, '2025-01-01'),
('ART-001', 900.00, '2025-06-01'), -- повысили себестоимость
('ART-001', 850.00, '2026-02-01'); -- снизили

Калибровка и точность данных

Данные это хорошо. Но точные ли они? Нужно с чем-то сверяться.

И тут у каждого маркетплейса свои плюсы и минусы.

Ozon

У Ozon есть отличный раздел “Баланс”. Заходим, выбираем период, смотрим. Если сходится с нашей структурой расходов, значит все отлично. Если не сходится — ищем причину, что не посчитали, и после доработки снова сверяем.

У Ozon есть особенность: много специфических статей расходов. Нельзя взять и сразу все посчитать. Чтобы это сделать, нужно знать, что искать. Статья расходов должна сначала “засветиться” в разделе “Баланс”. Увидели расхождение -> нашли причину -> добавили в ядро.

Так и получается, что по Ozon мы постоянно что-то добавляем в ядро. Обычно это уже мелкие статьи, например небольшие комиссии в разделе “Другие услуги и штрафы”.

Wildberries

У WB все по-другому. Тут еженедельные / ежедневные финансовые отчеты. Принцип похожий, но не так удобно, как в Ozon.

Если продажи сходятся по финансовым отчетам рубль в рубль, то сумма к перечислению иногда остается “мифической”. Например, на отрезке в неделю или месяц при сумме 1 000 000 может быть разница в пару сотен рублей. Что это и откуда берется до конца непонятно. Критично это или нет? Мы считаем, что нет.

Заказы выгружаются из воронки продаж. Сходятся в рубль. Тут все отлично, если бы не одно “но”: API-протокол воронки отдает данные всего за 7 дней. Это не вариант.

Но можно выгрузить историю воронки продаж за 1 год из личного кабинета. А это уже возможность.

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

И, кстати, что за график заказов и продаж на главной странице личного кабинета WB, для меня так и осталось загадкой. Он не сходится вообще ни с чем: ни с воронкой, ни с финансовыми отчетами, ни со “Сводным по продавцу”. Поэтому ориентироваться на него, на мой взгляд, не стоит.

Возникает вопрос: почему мы берем продажи из финансовых отчетов и заказы из воронки, ведь есть отдельные API-протоколы:

  • api/v1/supplier/orders — для заказов

  • api/v1/supplier/sales — для продаж

Да, это специальные методы для выгрузки заказов и продаж. Вот только выгружают они скорее данные “по мотивам”. Там выгружаются не все заказы. Почему — непонятно. Насколько помню, частично теряются заказы с постоплатой. Мы используем эти протоколы, но не для целей построения графиков заказов и продаж.

Выглядят данные правдоподобно, но ни с чем не стыкуются. Оказалось, проблема старая. WB о ней знают, но уже пару лет ничего не меняют.

Поэтому если у вас в сервисах аналитики не сходятся данные продаж и заказов, возможно, они выгружаются по этим протоколам. А там погрешность может быть порядка 10-15%.

Именно поэтому мы не берем их как основной источник.

Схема автоматизации

┌────────────────────────────────────────┐
│ Форма на сайте                         │
│ (Имя, email, API-токен)                │
└───────────────────┬────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────┐
│ Python-скрипт (оркестратор)            │
└───────────────────┬────────────────────┘
                    │
                    ├─► Проверка корректности API-ключей
                    ├─► Заведение клиента в PostgreSQL
                    ├─► Подключение дашборда в DataLens
                    ├─► Генерация уникальной ссылки на дашборд по токену клиента
                    ├─► Запуск скриптов выгрузки данных
                    └─► Отправка письма со ссылкой на дашборд

Мы решили давать доступ к дашборду через генерацию ссылки на нашем домене. Дашборд открывается во фрейме. Datalens недавно сделал платным заведение каждого клиента. Поэтому работа с доступами на стороне Datalens нам не подходила. Решили делать это на стороне нашего сервера. Но если возникает потребность настройки гибкого доступа на стороне Datalens, то это тоже можно сделать.

Ключевые моменты:

  1. Google таблицы для справочников. Себестоимость и планы — данные, которые пользователь вводит сам. Вместо отдельной админки подключили Google таблицы через API. Пользователь получает доступ к таблице, заполняет ее, скрипт выгружает данные в БД.

  2. Cron для ежедневной выгрузки. Каждое утро запускается update_all.sh, который по очереди вызывает скрипты выгрузки для всех пользователей.

  3. Размазываем запуск по времени. Не запускаем все выгрузки в одну секунду, а распределяем их по отрезку примерно 00:00-04:00 МСК.

Часть update_all.sh:

#!/bin/bash

# Начало выгрузки
echo "=== НАЧАЛО ЕЖЕДНЕВНОЙ ВЫГРУЗКИ WB: $(date) ===" >> "$LOG_FILE"
cd "$PROJECT_DIR" || exit 1

# 1. Справочники (не зависят ни от чего)
run_script "wb_cost_price.py"
run_script "wb_plan_sync.py"

# 2. Основные данные (API -> БД)
run_script "wb_orders.py"
run_script "wb_sales.py"
run_script "wb_finance_daily.py"
run_script "wb_paid_storage.py"
run_script "wb_stocks.py"
run_script "wb_prices.py"
run_script "wb_adv.py"
run_script "wb_funnel.py"

# 3. Пост-обработка (зависит от wb_sales + geo)
run_script "wb_sales_by_city.py"

# 4. Доп. данные
run_script "wb_weather.py"

# 5. Расчеты (зависит от wb_finance + wb_adv + wb_paid_storage + wb_cost_price)
run_script "wb_economic.py"

echo "=== ЗАВЕРШЕНО WB: $(date) ===" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"

Что пошло не так (и как исправили)

1. Google таблицы не держат нагрузку

Проблема: таблицы периодически отваливались, коннектор DataLens терял связь.
Решение: перешли на свой сервер с PostgreSQL. Скорость выросла, проблемы исчезли. Архитектура стала принципиально другой.

2. MySQL тормозила на JOIN

Проблема: запросы с несколькими JOIN на больших таблицах выполнялись 15-20 секунд.
Решение: миграция на PostgreSQL + оптимизация скриптов + перенос части обработки данных на сервер.
Результат: тот же запрос около 5 секунд.

3. Версионирование себестоимости

Проблема: первая версия хранила только текущую себестоимость. Исторические расчеты были неверны.
Решение: добавили поле start_date и корректную логику подбора действующей себестоимости.
Результат: корректный расчет себестоимости и прибыли без искажения исторических данных.

4. Масштабируемость DataLens

Проблема: в DataLens сложно автоматизировать некоторые процессы создания дашбордов через скрипты.
Решение: в процессе. Планируем протестировать Open Source версию DataLens на своем сервере, чтобы расширить автоматизацию.

Что в итоге

В итоге мы прошли путь от потребности в нормальной аналитике для себя до полноценного SaaS-продукта.

Спрос есть. Продукт интересен и, главное, полезен.

Аналитика маркетплейсов стала для нас отправной точкой. Дальше мы пошли в другие площадки и в разработку кастомных решений (в том числе под МойСклад и Retail SRM), расширили штат и продолжаем развивать и себя, и продукт.

Главный вывод: если есть источник данных, это можно оцифровать.

Демо итогового результата можно посмотреть тут.


К чему все это? Наверное, к тому, что обычная потребность в аналитике может вырасти во что-то большее. Дальше можно написать еще кучу всякой цыганщины про возможности, реализацию и прочий успешный успех, но незачем…

P.S. Если интересны технические детали — пишите в комментариях, расскажу подробнее.

Автор: alexgmu51

Источник

Оставить комментарий