Автоматизированная система сбора и визуализации аналитики маркетплейса Wildberries
Программа, которая по расписанию сама забирает все отчёты кабинета продавца Wildberries, складывает их в базу данных и выводит готовые сводные таблицы в Google Sheets — без ручного скачивания и копирования.

Слайд 1 из 7
ОПИСАНИЕ
ETL-конвейер на Python, полностью автоматизирующий сбор бизнес-данных продавца маркетплейса. Система самостоятельно собирает около двадцати типов отчётов (заказы и лента заказов, остатки, штрафы и удержания, скидки, комиссии, КТР, характеристики товаров, рекламные затраты, поставки FBW, поисковые запросы, платное хранение, тарифы складов, индексы цен, проблемные и заблокированные карточки, еженедельный финансовый отчёт), нормализует их и поддерживает в актуальном состоянии набор управленческих листов в Google Sheets. Кодовая база разделена на четыре слоя — авторизация и подготовка профилей, сбор сырых данных, загрузка в PostgreSQL, выгрузка в таблицы — плюс изолированный слой доступов и конфигурации.
Оркестрация
Корневой процесс работает в бесконечном цикле с настраиваемым интервалом и на каждой итерации последовательно запускает три этапа как отдельные процессы: сбор → база → выгрузка, а затем профилактическую очистку. Внутри каждого этапа диспетчер порождает дочерние скрипты и исполняет их параллельно, контролируя каждый индивидуальным тайм-аутом: зависший скрипт принудительно завершается, а связанные с ним подвисшие процессы браузера зачищаются. За счёт изоляции по процессам сбой одного отчёта не роняет ни соседние, ни конвейер в целом. Все процессы запускаются через общий раннер, обеспечивающий сквозной перехват ошибок (вынесен в отдельную работу).
Каналы сбора данных
Сбор данных идёт двумя каналами в зависимости от того, что отдаёт Wildberries. Где есть официальный API (реклама, поставки FBW) — используются REST-запросы с JWT-авторизацией, постраничная выгрузка, троттлинг под лимиты запросов и агрегация на стороне клиента. Отчёты, которых в API нет, добываются браузерной автоматизацией на Selenium: скрипт открывает реальный кабинет, инициирует серверное формирование Excel-отчёта за нужный период и скачивает готовый файл. Сценарии написаны под нестабильную и постоянно меняющуюся вёрстку личного кабинета: каскад резервных селекторов с приоритетом устойчивых атрибутов, клик с откатом на JavaScript, ожидание асинхронно формируемых отчётов через опрос статуса очереди, обход интерфейсных ловушек вроде баннеров, уводящих со страницы. Скачивание тяжёлых файлов отслеживается по появлению результата с фильтрацией незавершённых загрузок.
Пул профилей Chrome
Пул профилей Chrome — ключевое решение слоя сбора. Авторизация (вход по телефону и SMS через Selenium) выполняется один раз, после чего мастер-профиль клонируется в двадцать независимых профилей, и каждый загрузчик работает на своём. Это позволяет собирать отчёты параллельно и сохранять сессию между циклами. Профили обслуживаются отдельными утилитами: проверка статуса авторизации, безопасная очистка кэша и снятие lock-файлов перед каждым запуском, чтобы профиль не «залипал». Отдельная профилактика — усечение раздутых служебных файлов профиля: Chrome копит их от цикла к циклу, и без чистки профиль со временем перестаёт подниматься.
Слой данных PostgreSQL
Слой данных — PostgreSQL, единая схема, доступ через psycopg2 с настройками устойчивости соединения. DDL идемпотентен и живёт в коде: таблицы создаются при отсутствии, а новые поля отчётов подхватываются автоматической миграцией (ALTER TABLE ... ADD COLUMN IF NOT EXISTS) без ручного вмешательства. Запись — пакетная; справочник товаров обновляется через upsert. Данные не перезаписываются, а накапливаются снимками с меткой времени — база хранит историю, а выгрузка читает последний срез. Справочник товаров — опорная сущность: остальные таблицы строятся относительно него, так что строка есть у каждого товара даже при нулевых показателях.
Парсинг Excel-отчётов
Парсинг исходных Excel-отчётов устойчив к «грязным» файлам Wildberries: архивы читаются из памяти без распаковки, строка заголовков определяется эвристически, колонки сопоставляются по нормализованным названиям с резервом на позиционные индексы, числа аккуратно приводятся к типам (запятые, неразрывные пробелы, проценты, пустые значения). Самостоятельный по сложности модуль — еженедельный финансовый отчёт: суммы удержаний собираются из разных колонок в зависимости от вида, а сами виды заранее неизвестны и приходят длинными строками. Поскольку имя столбца в PostgreSQL ограничено по длине, каждому виду присваивается короткий ключ, а соответствие «ключ → полное название» хранится в отдельной таблице; начисления уровня аккаунта распределяются по товарам пропорционально.
Выгрузка в Google Sheets
Выгрузка в Google Sheets выполнена через сервисный аккаунт по принципу атомарной публикации: каждый скрипт пишет во временный лист, и только после успешного завершения всех загрузок основные листы атомарно подменяются — пользователь никогда не видит полузаполненную таблицу. Форматирование (числовые форматы, проценты, визуальные разделители групп) задаётся пакетными запросами к API. Часть листов собирается не зеркалированием одного отчёта, а соединением нескольких таблиц с группировкой и сортировкой; для отдельных листов вызывается развёрнутое веб-приложение Google Apps Script для финального оформления.
Новые отчёты и расчётные листы
Набор управленческих листов вырос за счёт отчётов, которых в исходной версии не было: индексы цен, проблемные и заблокированные карточки, лента заказов, платное хранение и тарифы складов, справочник предметов и категорий WB. Часть новых листов — не зеркало отчёта, а расчёт поверх нескольких таблиц.
Лист себестоимости логистики и хранения считает, во сколько обходится товар до продажи. Он соединяет платное хранение (единственный отчёт, где видно, что лежит коробом, а что монопаллетой), тарифы складов, остатки, КТР и справочник товаров, и добавляет расход, которого в тарифах WB нет и быть не может, — нашу собственную доставку до склада. Цена перевозки паллеты до каждого склада — ручные данные директора, вместимость паллеты ведётся отдельной таблицей, которая при потере исходного файла подхватывается из слепка в коде. Цена паллеты усредняется по складам взвешенно, по фактическим остаткам артикула: везти в Коледино за 781 ₽ и во Владивосток за 18 750 ₽ — разные деньги, и простая средняя по трём десяткам складов повесила бы владивостокскую цену на товар, который туда никогда не ездил. Склады, закрытые после пожаров, из расчёта выкидываются: WB продолжает показывать их в отчёте хранения, и без фильтра часть веса легла бы на товар, которого физически нет.
Второй расчётный лист — лестница скидки Wildberries. Скидка, которую площадка вешает поверх нашей цены, меняется не плавно, а ступенями по цене товара, и на границе ступени цена для покупателя падает при росте нашей: подняв цену с 2000 до 2001 ₽, мы получаем на рубль больше, а покупатель платит на 170 меньше. Сами пороги WB не публикует, поэтому лист берёт сетку круглых чисел, а величину скидки на каждой ступени считает по факту — по нашим же товарам за последние дни, в разрезе «предмет × схема поставки». Лишний порог в сетке безвреден: если на нём ничего не меняется, соседние ступени совпадут, и это видно прямо в строке. Раньше лист был посписочным, по артикулам; сводка отвечает на тот же вопрос сразу по всему каталогу.
Заказы за вчера перестали зависеть от одного отчёта: они собираются из двух разных отчётов кабинета, и в лист попадает та цифра, которая полнее. По тому же правилу выбирается источник для заказов в разрезе складов отгрузки — отчёты WB расходятся между собой, и раньше это молча занижало цифры.
Работа над устойчивостью
Отдельным направлением шла работа над устойчивостью — по следам того, что показал журнал ошибок. Скрипты выгрузки стартуют почти одновременно и упираются в один сервисный аккаунт Google, а тот на залп отвечает отказом по квоте (лимит — 60 запросов записи в минуту): в один из дней таких вспышек было шесть, и они дали 116 ошибок. Теперь все выгрузки ходят через общий клиент с повторами, навешанными на единственный метод, через который в библиотеке проходят все обращения к API, — оборачивать каждый вызов в каждом скрипте не понадобилось.
Скачивание отчётов перестало путать файлы: папка загрузок общая, отчёты качаются параллельно, и раньше скрипт забирал первый появившийся файл, чей угодно. Теперь каждый отчёт ждёт файл по своей маске — той же, по которой его потом ищет загрузчик в базу. Ошибки пишутся в журнал целиком, без обрезки, иначе стек терялся ровно там, где он был нужен. Сбои сгруппированы по смыслу: если хотя бы один из четырёх отчётов по остаткам не скачался, в базу не идёт ни один из них — неполный срез остатков хуже, чем его отсутствие.
Сквозные решения
Сквозные решения: единый текстовый протокол диагностики (успех / удаление / ошибка с типом, сообщением и стеком), на который опирается подсистема логирования; все секреты и внешние доступы вынесены из бизнес-логики и переопределяются через переменные окружения; время везде приведено к московской зоне; версии зависимостей зафиксированы.
ИСПОЛЬЗУЕМЫЕ ИНСТРУМЕНТЫ
- Python 3
- Selenium
- WebDriver
- Chrome/ChromeDriver
- Chrome DevTools Protocol
- pandas
- openpyxl
- PostgreSQL
- psycopg2
- gspread (Google Sheets API)
- Google service account
- Google Apps Script
- requests
- REST API Wildberries (JWT-авторизация)
- pytz
- subprocess (многопроцессная оркестрация)
- Git
РЕЗУЛЬТАТ
- Построен сквозной ETL-конвейер «сбор → PostgreSQL → Google Sheets», работающий по расписанию без участия человека.
- Реализован оркестратор из независимых процессов с параллельным запуском, индивидуальными тайм-аутами и зачисткой зависших процессов браузера — отказ одного отчёта не ломает конвейер.
- Автоматизирован сбор около двадцати типов отчётов двумя каналами: официальный REST API Wildberries и браузерная автоматизация на Selenium для данных, недоступных через API.
- Создан пул из двадцати предавторизованных профилей Chrome с разовым входом по SMS и закреплением профиля за каждым загрузчиком — для параллельного сбора и сохранения сессии.
- Разработаны устойчивые к смене вёрстки сценарии Selenium: каскад селекторов, откат на JavaScript-клик, ожидание асинхронно формируемых отчётов, обход интерфейсных ловушек, контроль скачивания файлов.
- Спроектирована схема БД в PostgreSQL с идемпотентным созданием таблиц, автоматической миграцией структуры, пакетной вставкой и upsert-ом справочника; данные хранятся снимками во времени.
- Реализован устойчивый парсер Excel/ZIP на pandas с эвристическим разбором заголовков и безопасным приведением типов.
- Решена нетривиальная задача финансового отчёта: динамический набор видов удержаний с генерацией ключей колонок в обход ограничений PostgreSQL, таблица-справочник и пропорциональное распределение общих начислений.
- Внедрён атомарный механизм публикации в Google Sheets через временные листы, программное форматирование и постобработку через Google Apps Script.
- Реализована сборка сводных листов соединением нескольких таблиц с группировкой и сортировкой.
- Добавлены отчёты, которых не было в первой версии: индексы цен, проблемные и заблокированные карточки, лента заказов, платное хранение, тарифы складов, справочник предметов и категорий WB.
- Построен лист себестоимости логистики и хранения: соединение шести источников, собственная доставка до склада со взвешиванием цены паллеты по фактическим остаткам и исключением закрытых складов.
- Построена сводка ступеней скидки Wildberries: непубликуемые пороги восстанавливаются по фактическим данным каталога в разрезе «предмет × схема».
- Заказы за вчера сверяются по двум независимым отчётам, в лист идёт более полная цифра — расхождение отчётов WB перестало занижать показатели.
- Устранён массовый источник сбоев выгрузки: общий клиент Google Sheets с повторами убрал отказы по квоте, дававшие свыше сотни ошибок за день.
- Параллельное скачивание перестало путать файлы — каждый отчёт забирает свой по маске; ошибки пишутся в журнал целиком, а неполный срез остатков не попадает в базу вовсе.
- Все секреты и доступы вынесены в отдельный слой с переопределением через переменные окружения.
AI АССИСТЕНТ
Задать вопрос по этой работе