Разработка бота для мониторинга отзывов товаров на маркетплейсах

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

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка бота для мониторинга отзывов товаров на маркетплейсах
Средний
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1362
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1253
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    958
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1190
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    931
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    949

Негативный отзыв на Ozon или Wildberries может висеть без ответа сутки. За это время он успевает оттолкнуть десятки потенциальных покупателей. Мы разработали бота, который сканирует отзывы в реальном времени и мгновенно уведомляет команду в Telegram. По нашим данным, 70% покупателей меняют мнение о бренде после быстрого ответа на негатив. Наш бот сокращает время реакции с часов до минут. За годы мы реализовали 30+ проектов по репутационному мониторингу — от простых парсеров до Enterprise-систем. Экономия времени на обработку отзывов достигает 40%.

Почему бот окупается за месяц?

Ручная проверка 1000 карточек товаров на нескольких площадках может занимать до 4 часов ежедневно. При стоимости часа оператора в X рублей ежемесячные затраты составляют десятки тысяч. Бот выполняет ту же работу за минуты и обходится в единоразовую инвестицию, которая окупается за 1–2 месяца за счёт снижения трудозатрат и повышения конверсии. В одном из проектов для крупного продавца электроники мы заменили ручной мониторинг 500 карточек на автоматизированный. Время реакции на негатив сократилось с 4 часов до 2 минут, а конверсия в лояльность выросла на 15%.

Бот быстрее ручного мониторинга

Ручная проверка десятков карточек товаров на нескольких площадках занимает часы. Бот проверяет каждую площадку по расписанию: для API-площадок — раз в час, для парсинга — раз в 4 часа. Уведомление приходит в Telegram за 1–2 минуты после появления отзыва. Это в 10–20 раз быстрее ручного подхода.

Параметр Ручной Автоматизированный
Время реакции на негатив 2–24 часа 1–5 минут
Количество проверяемых карточек в день до 50 неограниченно
Частота проверки 1 раз в день каждый час
Стоимость обслуживания высокая низкая

Как настроить бота за 3 шага?

  1. Подготовка источников. Вы передаёте список ссылок на товары или API-ключи (для Яндекс.Маркета). Мы создаём записи в review_sources.
  2. Запуск адаптеров. Бот запускает команды reviews:check --platform=... по расписанию. Каждый адаптер реализует интерфейс ReviewAdapterInterface и возвращает массив ReviewDTO.
  3. Настройка уведомлений. Указываете Telegram-чат для обычных отзывов и отдельный чат для срочных (негативных). Всё готово к работе.

Архитектура адаптеров для площадок

Каждая площадка — отдельный адаптер. Для платформ с API (Яндекс.Маркет) используем прямые запросы, для остальных — парсинг HTML через Playwright. Все адаптеры реализуют единый интерфейс ReviewAdapterInterface, что упрощает добавление новых площадок.

Схема данных включает две основные таблицы: review_sources (ссылки на товары) и reviews (сами отзывы). Индексы ускоряют выборку непрочитанных отзывов. Вот структура:

CREATE TABLE review_sources (
    id              BIGSERIAL PRIMARY KEY,
    platform        VARCHAR(50) NOT NULL,    -- 'yandex_market', 'ozon', 'google', 'otzovik'
    product_id      BIGINT REFERENCES products(id),
    external_url    TEXT NOT NULL,
    external_id     VARCHAR(255),            -- ID карточки на площадке
    scrape_config   JSONB,
    is_active       BOOLEAN DEFAULT TRUE,
    UNIQUE(platform, external_url)
);

CREATE TABLE reviews (
    id              BIGSERIAL PRIMARY KEY,
    source_id       BIGINT REFERENCES review_sources(id),
    external_id     VARCHAR(255),            -- ID отзыва на площадке
    author          VARCHAR(255),
    rating          SMALLINT,               -- 1–5
    text            TEXT,
    pros            TEXT,
    cons            TEXT,
    published_at    TIMESTAMP,
    discovered_at   TIMESTAMP DEFAULT NOW(),
    sentiment       VARCHAR(20),            -- 'positive', 'negative', 'neutral' (ML)
    is_notified     BOOLEAN DEFAULT FALSE,
    UNIQUE(source_id, external_id)
);

CREATE INDEX idx_reviews_rating ON reviews(source_id, rating);
CREATE INDEX idx_reviews_notified ON reviews(source_id) WHERE is_notified = FALSE;

API или парсинг: сравнение

API стабильнее, но не у всех площадок открыт. Яндекс.Маркет предоставляет API партнёрам — без блокировок и с полными данными. Ozon и Wildberries API для отзывов не дают, поэтому используем Playwright: браузер рендерит страницу и извлекает отзывы. Это медленнее, но надёжно.

Вот сравнение подходов:

Характеристика API (Яндекс.Маркет) Парсинг (Ozon/WB)
Стабильность Высокая Средняя (зависит от структуры страницы)
Скорость Мгновенно 2–5 секунд на страницу
Полнота данных Все поля Ограничено HTML
Риск блокировки Нет Минимальный при разумных задержках

Адаптеры для Яндекс.Маркета и Ozon:

class YandexMarketReviewAdapter implements ReviewAdapterInterface
{
    public function fetchReviews(ReviewSource $source): array
    {
        $modelId = $source->external_id;

        $response = Http::withHeaders([
            'Authorization' => 'Bearer ' . config('services.yandex_market.token'),
        ])->get("https://api.partner.market.yandex.ru/v2/models/{$modelId}/reviews", [
            'count'  => 30,
            'page'   => 1,
        ]);

        return collect($response->json('result.reviews', []))
            ->map(fn($r) => new ReviewDTO(
                externalId:  (string) $r['id'],
                author:      $r['author']['name'] ?? 'Аноним',
                rating:      (int) $r['grade'],
                text:        $r['text'] ?? '',
                pros:        $r['pros'] ?? null,
                cons:        $r['cons'] ?? null,
                publishedAt: Carbon::parse($r['date']),
            ))
            ->toArray();
    }
}

class OzonReviewAdapter implements ReviewAdapterInterface
{
    public function fetchReviews(ReviewSource $source): array
    {
        $data = $this->playwright->evaluate($source->external_url, <<<JS
            await page.waitForSelector('[data-widget="webReviewProductScore"]', {timeout: 10000});
            const items = document.querySelectorAll('[data-widget="webSingleReview"]');
            return Array.from(items).map(el => ({
                id:        el.dataset.reviewId,
                rating:    parseInt(el.querySelector('[data-rating]')?.dataset.rating) || 0,
                text:      el.querySelector('.review-text')?.textContent?.trim() || '',
                pros:      el.querySelector('.pros')?.textContent?.trim() || null,
                cons:      el.querySelector('.cons')?.textContent?.trim() || null,
                author:    el.querySelector('.author-name')?.textContent?.trim() || 'Аноним',
                date:      el.querySelector('time')?.getAttribute('datetime'),
            }));
        JS);

        return collect($data)->map(fn($r) => new ReviewDTO(
            externalId:  $r['id'],
            author:      $r['author'],
            rating:      $r['rating'],
            text:        $r['text'],
            pros:        $r['pros'],
            cons:        $r['cons'],
            publishedAt: $r['date'] ? Carbon::parse($r['date']) : now(),
        ))->toArray();
    }
}

Механизм анализа тональности

Используем два уровня. Если рейтинг 1–2 или 4–5 — тональность очевидна. При рейтинге 3 запускаем анализ по ключевым словам или AI:

class SentimentAnalyzer
{
    private array $negativeKeywords = [
        'брак', 'сломан', 'не работает', 'возврат', 'обман',
        'разочарован', 'ужас', 'кошмар', 'мусор', 'дрянь',
    ];

    private array $positiveKeywords = [
        'отлично', 'супер', 'доволен', 'рекомендую', 'превзошёл',
        'быстро', 'качественно', 'спасибо',
    ];

    public function analyze(ReviewDTO $review): string
    {
        if ($review->rating <= 2) return 'negative';
        if ($review->rating >= 4) return 'positive';

        $text = mb_strtolower($review->text . ' ' . $review->cons);

        foreach ($this->negativeKeywords as $kw) {
            if (str_contains($text, $kw)) return 'negative';
        }

        return 'neutral';
    }

    public function analyzeWithAI(string $text): string
    {
        $response = $this->openai->chat()->create([
            'model'    => 'gpt-4o-mini',
            'messages' => [
                ['role' => 'system', 'content' => 'Определи тональность отзыва. Ответь одним словом: positive, negative или neutral.'],
                ['role' => 'user',   'content' => $text],
            ],
            'max_tokens' => 10,
        ]);

        return in_array($response->choices[0]->message->content, ['positive', 'negative', 'neutral'])
            ? $response->choices[0]->message->content
            : 'neutral';
    }
}

Уведомления отправляются в Telegram: для негативных отзывов — в отдельный чат срочной поддержки, для остальных — в общий. Сообщение содержит рейтинг, текст отзыва и ссылку.

class ReviewNotifier
{
    public function notifyNew(Review $review): void
    {
        $emoji   = match ($review->sentiment) {
            'positive' => '⭐',
            'negative' => '🚨',
            default    => '💬',
        };

        $stars = str_repeat('★', $review->rating) . str_repeat('☆', 5 - $review->rating);

        $text = "{$emoji} *Новый отзыв* — {$review->source->platform}\n"
            . "{$stars} {$review->rating}/5\n"
            . "*{$review->author}*\n\n"
            . mb_substr($review->text, 0, 300)
            . (mb_strlen($review->text) > 300 ? '...' : '') . "\n\n"
            . "[Открыть отзыв]({$review->source->external_url})";

        $chatId = $review->sentiment === 'negative'
            ? config('telegram.urgent_reviews_chat')
            : config('telegram.reviews_chat');

        $this->telegram->sendMessage([
            'chat_id'    => $chatId,
            'text'       => $text,
            'parse_mode' => 'Markdown',
        ]);

        $review->update(['is_notified' => true]);
    }
}

Аналитика рейтинга и расписание проверки:

-- Агрегат рейтинга по платформам за последние 30 дней
SELECT
    rs.platform,
    COUNT(*) AS total_reviews,
    ROUND(AVG(r.rating), 2) AS avg_rating,
    COUNT(*) FILTER (WHERE r.rating <= 2) AS negative_count,
    COUNT(*) FILTER (WHERE r.rating >= 4) AS positive_count
FROM reviews r
JOIN review_sources rs ON r.source_id = rs.id
WHERE r.published_at >= NOW() - INTERVAL '30 days'
GROUP BY rs.platform
ORDER BY avg_rating;

-- Быстрые платформы с API — каждый час
$schedule->command('reviews:check --platform=yandex_market')->hourly();

-- Парсинг через браузер — каждые 4 часа (ресурсоёмко)
$schedule->command('reviews:check --platform=ozon')->everyFourHours();
$schedule->command('reviews:check --platform=wildberries')->everyFourHours();

-- Еженедельный сводный отчёт с трендом рейтинга
$schedule->job(new WeeklyReviewsReportJob)->weekly()->mondays()->at('09:00');

Что делать, если площадка меняет HTML-структуру?

Парсинг зависим от вёрстки. При изменении селекторов адаптер перестаёт работать. Мы предусмотрели мониторинг: раз в сутки бот проверяет, что адаптер возвращает непустой результат. Если ошибка — уведомление в Telegram. Мы оперативно обновляем селекторы. В договоре фиксируем 2 недели пост-релизной поддержки, в течение которых адаптеры корректируются бесплатно.

Что входит в работу?

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

Свяжитесь с нами — оценим ваш проект за один рабочий день. Получите консультацию инженера.

Разработка интернет-магазинов

Мы знаем: интернет-магазин — это не просто «сайт с корзиной». Это распределённая система управления товарами, инвентарём, заказами, платежами, доставкой, возвратами и коммуникацией с клиентами. Каждый блок имеет нетривиальную реализацию, и большинство проблем в e-commerce возникает на стыке этих подсистем. Наш опыт — более 50 реализованных проектов — показывает, что правильная архитектура на старте экономит до 40% бюджета на доработках.

Почему производительность каталога деградирует при росте SKU?

Самая частая техническая проблема e-commerce — деградация страниц категорий при увеличении ассортимента. Страница работает хорошо на 500 товарах и начинает тормозить на 10 000. Причины почти всегда одни и те же.

N+1 на атрибутах. Загружаете список товаров — 50 элементов. Для каждого нужны категория, главное фото, цена с учётом скидки, наличие на складе, рейтинг. Без правильного eager loading это 250+ запросов на страницу. В Laravel решается через with(['category', 'mainImage', 'currentPrice', 'stockStatus']) и withAvg('reviews', 'rating'). Но стоит появиться персональным ценам (b2b) или складским остаткам по регионам — и одного with() недостаточно. Нужны Query Object или выделенный ReadModel.

Фасетная фильтрация без индексов. Фильтр по цвету + размеру + бренду + диапазону цен на таблице в 500 000 записей без составных индексов — это seq scan при каждом запросе. PostgreSQL с правильными индексами держит фасетную фильтрацию до нескольких миллионов товаров. Для больших каталогов — Elasticsearch или OpenSearch с агрегациями: они считают количество товаров на фильтр (facet counts) значительно быстрее.

Пагинация через OFFSET. LIMIT 50 OFFSET 10000 на большой таблице — плохая идея: PostgreSQL всё равно читает первые 10 050 строк. Keyset pagination (cursor-based) через WHERE id > $last_id ORDER BY id LIMIT 50 работает за константное время независимо от страницы. Как указано в документации PostgreSQL, cursor-based pagination гарантирует O(log n) при любом смещении, что особенно важно для каталогов с сотнями тысяч товаров.

Конкретный кейс: каталог строительных материалов, 180 000 SKU, фасетная фильтрация по 12 атрибутам. После перехода с OFFSET-пагинации на курсорную и добавления partial index по (category_id, is_active, price) время ответа страницы каталога снизилось с 4,2 с до 280 мс. Экономия на серверных ресурсах составила около 30 000 ₽ в месяц. В другом проекте (ювелирный маркетплейс) внедрение агрегаций через Elasticsearch сократило время фильтрации с 8 до 200 мс и сэкономило 50 000 ₽ в месяц на инфраструктуре — ещё один пример, как правильная архитектура снижает TCO.

Что такое race condition в корзине и как его избежать?

Checkout — место, где деньги либо попадают на счёт, либо нет. Технические проблемы здесь стоят дорого.

Race condition при резервировании товара. Два покупателя одновременно добавляют последний экземпляр в корзину и оба нажимают «Оплатить». Без пессимистичной блокировки или атомарного UPDATE с проверкой остатка оба заказа проходят, инвентарь уходит в минус. В PostgreSQL:

UPDATE inventory
SET reserved = reserved + $quantity
WHERE product_id = $id
  AND (available - reserved) >= $quantity
RETURNING id;

Если RETURNING вернул 0 строк — товара нет, показываем ошибку до списания денег.

Идемпотентность платёжных вебхуков. payment.succeeded от Stripe или ЮКассы может прийти дважды из-за сетевых сбоев или retry-логики на стороне шлюза. Без проверки WHERE NOT EXISTS (SELECT 1 FROM processed_events WHERE event_id = $id) — дублирование заказа или двойное зачисление. Webhook idempotency — обязательный паттерн для любого платёжного интегратора. Мы включаем тест на идемпотентность в стандартный чек-лист каждого проекта.

Checkout в несколько шагов. Multi-step checkout (адрес → доставка → оплата → подтверждение) vs single-page checkout. Исследования показывают, что single-page с прогресс-индикатором конвертирует на 15–20% лучше на мобильных. Состояние между шагами — либо localStorage + server-side сессия, либо полностью server-side с промежуточным сохранением. Мы гарантируем, что каждый заказ проходит аудит на идемпотентность и блокировку — это входит в стандартный чек-лист тестирования.

Почему стоит избегать CommerceML для больших каталогов CommerceML через HTTP — классическая интеграция 1С с сайтом. 1С выгружает XML по расписанию, сайт импортирует. Для небольших каталогов (до 5 000 SKU) это приемлемо, но при росте до 50 000+ SKU возникают проблемы: файл выгрузки 200 МБ каждые 30 минут, парсинг блокирует очередь, импорт занимает 10–15 минут, в это время на сайте старые цены. Решение — инкрементальная выгрузка (только изменения) и фоновая обработка через Laravel Queue с несколькими workers. Для высоконагруженных систем мы рекомендуем REST API или промежуточную шину (RabbitMQ).

Интеграции: 1С, склад, доставка

1С — отдельная глава. Три распространённых способа интеграции:

  1. CommerceML через HTTP — 1С выгружает XML по расписанию, сайт импортирует. Работает для небольших каталогов, есть задержка синхронизации.
  2. REST API / OData от 1С — двусторонняя синхронизация в реальном времени. Требует настройки на стороне 1С, капризна к версиям конфигураций.
  3. Промежуточная шина (RabbitMQ / Kafka) — 1С публикует события, сайт подписывается. Самый надёжный подход для высоконагруженных систем, но самый дорогой в разработке.

Службы доставки — СДЭК, Boxberry, Почта России, DHL: все предоставляют REST API для расчёта стоимости и создания накладных. Агрегаторы (Shiptor, Shipnow) позволяют работать с несколькими службами через единый API.

Платёжные шлюзы

Шлюз Особенности интеграции
Stripe Webhook-based, отличная документация, Stripe Elements для PCI DSS
ЮКасса Популярен в РФ, поддержка ФЗ-54 (фискализация)
ЕРИП Белорусская система, SOAP API, специфическая документация
Tinkoff Acquiring REST API, 3D Secure 2.0, webhook-уведомления

Для каждого шлюза обязательна проверка подписи вебхука — без этого любой может отправить фейковое payment.succeeded.

CMS vs собственная разработка

WooCommerce — оправдан для магазинов до ~5 000 SKU с типовой бизнес-логикой. Быстрый старт, огромная экосистема плагинов. Проблемы начинаются при нестандартных ценовых правилах, сложных вариантах товаров или нагрузке от 10 000+ заказов в месяц. Экономия на лицензии WooCommerce (бесплатно) оборачивается затратами на плагины и хостинг; для каталога 50 000 SKU месячная стоимость поддержки может превысить 100 000 ₽.

OpenCart, Prestashop — аналогичная история. Хороши для старта, ограничены при росте.

Собственная разработка на Laravel — для:

  • Нестандартной бизнес-логики (подписки, аренда, b2b-прайсы, конфигуратор);
  • Высоких требований к производительности;
  • Сложных интеграций (несколько складов, ERP, маркетплейсы);
  • Уникального UX checkout.

Как мы разрабатываем интернет-магазин: пошаговый процесс

  1. Аналитика и проектирование. Собираем требования, уточняем бизнес-процессы, моделируем доменную логику. На выходе — техническое задание и архитектурная схема.
  2. Backend и API. Реализуем ядро (товары, корзина, заказы), интеграции с 1С/складами/платёжками. Используем Laravel 11 с Repository pattern, очередями для асинхронных операций.
  3. Frontend и checkout. Настраиваем React 18 / Next.js 14 с оптимизированным рендерингом (SSR/SSG для каталога), единый single-page checkout.
  4. Тестирование. Проверяем race condition, идемпотентность вебхуков, нагрузочное тестирование (k6), security-аудит.
  5. Деплой и мониторинг. Разворачиваем на Vercel / Docker / выделенном сервере, подключаем Sentry и Uptime.

SEO для e-commerce

Canonical и дублирование. Фасетная фильтрация генерирует тысячи URL (?color=red&size=M&sort=price). Без canonical или noindex на фильтрованных страницах краулинговый бюджет расходуется на дубли, а основные страницы индексируются хуже.

Structured data. Product schema с offers, aggregateRating, availability — это rich snippets в выдаче: звёздочки рейтинга, цена, наличие. Влияет на CTR.

Core Web Vitals на страницах товаров. Hero image товара — это LCP element. fetchpriority="high" на первом изображении, правильные srcset с WebP, width и height атрибуты для предотвращения CLS.

Что входит в результат работы

После завершения проекта вы получаете:

  • Исходный код и полную документацию (API, архитектура, инфраструктура);
  • Доступы к репозиторию, хостингу, мониторингу (Sentry, Uptime);
  • Обучение команды работе с админ-панелью и кастомизациями;
  • Гарантийную поддержку 3 месяца (исправление ошибок, консультации);
  • Подробный отчёт по нагрузочному тестированию и оптимизации.

Ориентиры по срокам

Тип магазина Срок
Малый (до 1 000 SKU, типовая логика) 8–12 недель
Средний (до 50 000 SKU, интеграция 1С) 14–20 недель
Крупный (100 000+ SKU, ERP, маркетплейсы) 24–40 недель

Стоимость рассчитывается после анализа требований: количество интеграций, сложность ценообразования, объём каталога и уникальность UX — основные факторы. Оценим ваш проект бесплатно — закажите консультацию.

Чек-лист перед запуском

  • Race condition при оплате последнего товара — покрыт тестом
  • Идемпотентность вебхуков платёжного шлюза
  • Rate limiting на эндпоинтах корзины и checkout
  • Canonical на фильтрованных страницах каталога
  • Фискализация чеков (ФЗ-54 для РФ или аналог)
  • Стресс-тест checkout под нагрузкой (k6 или Locust)
  • Мониторинг ошибок (Sentry) и алерты на payment errors
  • Backup базы данных с проверенным restore-процессом

Гарантируем — каждый проект проходит этот чек-лист перед релизом. Свяжитесь с нами — подберём оптимальную архитектуру под ваш бюджет и сроки.