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

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

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

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

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

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

Интернет-магазины и маркетплейсыСредний~3-5 дней
Часто задаваемые вопросы

Негативный отзыв на 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 рабочих дней. Стоимость рассчитывается индивидуально под ваш стек и количество площадок. Мы гарантируем стабильную работу бота при соблюдении лимитов площадок.

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

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

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

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1543
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1395
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1374
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060