Как увеличить охват фраз Wildberries и ускорить бота
Оптимизация Telegram-бота для Wildberries: гибридная архитектура с мгновенными ответами из OpenSearch, фоновый сбор данных, морфология для русского языка и антибот-защита.
Как увеличить охват поисковых фраз Wildberries и ускорить работу бота?
Мы разрабатываем Telegram‑бота, который по артикулу WB (nmId) или ссылке показывает, по каким фразам карточка ранжируется, с позицией в органике и указанием на рекламу.
Ограничения:
- Без продавческих токенов
- Без Jam
- Используем только публичные/фронтовые JSON (DevTools), autosuggest, безопасный скрапинг SERP и catalog‑ads
Текущая реализация (Node.js, TypeScript):
- BFS по подсказкам (autosuggest) + нормализация
- Валидация фраз страницами SERP …/exactmatch/…search?query=&page= (парсинг products[*].id/name/brand и позиция нашего nmId)
- Проверка рекламы через https://catalog-ads.wildberries.ru/api/v5/search?k… (если наш nmId среди рекламных — помечаем ⚡; CPM чаще null)
- Троттлинг Bottleneck (~0.45 RPS/хост), axios-retry (экспонента + джиттер)
- Redis TTL‑кэш (suggest/serp/card/ads), базовый скоринг
- Telegram UX: nmId‑only → отдаём топ‑5 + «Показать больше»
Проблемы:
- Фраз меньше, чем у конкурентов (они показывают ~×3–×4 больше)
- Время первого ответа: конкурент отдаёт топ‑5 «мгновенно», а мы ждём BFS/валидацию
Гипотеза: мгновенный ответ из индекса/кэша (OpenSearch с russian+shingle(2,3) и Redis Top‑K), а глубокий сбор — в фоне.
Конкретные вопросы:
-
Правильная схема прединдекса для «nmId → фразы (2–3‑граммы, синонимы, словоформы) → быстрый top‑K»:
- Какие анализаторы/настройки используете в OpenSearch/ES для русского?
- Шинглы vs edge_ngram, стоп‑слова, нормализация
-
Параметры SERP‑валидации:
- Какие страницы, dest/spp/lang/curr использовать?
- Толерантный парсер версий (v18/v4): что сейчас стабильно работает в проде?
- Мы используем exactmatch‑роуты из фронта, как советуют гайды/разборы DevTools
-
Реклама:
- Подтверждаем факт через catalog‑ads …/api/v5/search?keyword=
- Видели ли вы стабильные поля CPM публично, или считаем null и живём с эвристикой?
-
Морфология:
- Что надёжнее для продакшена под русский — pymorphy2 сервисом или достаточно токенизации + шинглов?
-
Антибот и скорость:
- Какой RPS/хост и backoff даёт наилучший «урожай» без 429/403?
- Имеет ли смысл пул IP/прокси при соблюдении robots/ToS?
-
Архитектура очередей (BullMQ):
- Как лучше строить пайплайн (collect → verify → rank → persist), чтобы мгновенно отдавать топ‑5 и фоново донаполнять?
Контекст: мы используем публичные фронтовые данные, подсмотренные в DevTools (как в профильных разборах), и не трогаем seller API; цель — легальный, «мягкий» сбор с кэшем и троттлингом.
Будем признательны за рабочие настройки анализаторов, примеры маппингов, витрины индекса и цифры по RPS/TTL из реальной практики.
Основная стратегия улучшения охвата и скорости вашего Telegram‑бота для Wildberries заключается в внедрении гибридной архитектуры: мгновенные ответы из прединдексированных данных и фоновый глубокий сбор. Для этого используйте OpenSearch с русскоязычными анализаторами, оптимизируйте параметры SERP‑валидации через публичные эндпоинты, внедрите систему очередей с приоритетами и настройте оптимальные троттлинги для балансировки скорости и стабильности.
Содержание
- Настройка прединдекса для мгновенных ответов
- Оптимизация параметров SERP‑валидации
- Работа с рекламными объявлениями
- Морфологическая обработка для русского языка
- Антибот‑защита и производительность
- Архитектура очередей и кэширования
Настройка прединдекса для мгновенных ответов
Для создания эффективной схемы прединдекса «nmId → фразы → быстрый top‑K» рекомендуется использовать следующие анализаторы в OpenSearch:
Анализаторы для русского языка
{
"settings": {
"analysis": {
"analyzer": {
"russian_shingle": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"russian_stop",
"snowball_russian",
"shingle"
]
},
"russian_edge_ngram": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"russian_stop",
"snowball_russian",
"edge_ngram"
]
}
},
"filter": {
"russian_stop": {
"type": "stop",
"stopwords": "_russian_"
},
"edge_ngram": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 5
},
"shingle": {
"type": "shingle",
"min_shingle_size": 2,
"max_shingle_size": 3,
"output_unigrams": false
}
}
}
}
}
Шинглы vs Edge NGram
- Шинглы лучше подходят для фразового поиска и работы с n‑граммами.
- Edge NGram эффективен для автодополнения и поиска по префиксам.
- Для вашей задачи рекомендуется комбинировать оба подхода: шинглы для основного поиска и edge_ngram для мгновенных подсказок.
Нормализация и стоп‑слова
Используйте Snowball Stemmer для русского языка, но сохраняйте оригинальные формы для точного отображения. Настройте кастомный список стоп‑слов, исключая важные коммерческие термины.
Оптимизация параметров SERP‑валидации
Параметры запросов SERP
Для валидации фраз используйте следующие параметры:
// Оптимальные параметры для запросов
const serpParams = {
lang: 'ru',
curr: 'rub',
dest: '-1257786', // Москва
spp: 30, // количество товаров на странице
local_present: '1'
};
// Пример запроса
const searchUrl = `https://search.wildberries.ru/exactmatch/catalog.aspx?query=${encodeURIComponent(query)}&page=1&${new URLSearchParams(serpParams)}`;
Стабильные версии парсинга
Исходя из практики разработчиков, наиболее стабильные подходы:
-
Версия v18 (настоящее время):
- URL:
https://search.wildberries.ru/exactmatch/catalog.aspx - Парсинг:
products[*].id,products[*].name,products[*].brand,products[*].price
- URL:
-
Резервная версия v4:
- Использовать в случае проблем с v18
- Структура данных может отличаться, требует адаптивного парсера
Толерантный парсер
class WildberriesParser {
async parseSerp(url, nmId) {
try {
const response = await this.fetchWithRetry(url);
const v18Data = this.tryParseV18(response);
if (v18Data) return v18Data;
const v4Data = this.tryParseV4(response);
return v4Data;
} catch (error) {
this.logError('SERP parsing failed', error);
return null;
}
}
}
Работа с рекламными объявлениями
Проверка рекламы через catalog‑ads
async function checkAdPresence(nmId, query) {
const adsUrl = `https://catalog-ads.wildberries.ru/api/v5/search?keyword=${encodeURIComponent(query)}&limit=50`;
try {
const response = await axios.get(adsUrl);
const adItems = response.data.cards || [];
const isAdvertised = adItems.some(item => item.nmId === nmId);
const cpm = this.extractCPM(response.data);
return {
isAdvertised,
cpm: cpm || null,
confidence: this.calculateAdConfidence(response.data)
};
} catch (error) {
return { isAdvertised: false, cpm: null, error: error.message };
}
}
Поля CPM в публичном API
Согласно исследованиям, поле CPM в публичном API часто возвращает null или отсутствует. Это связано с политикой Wildberries по скрытию рекламных ставок. Рекомендуется использовать эвристики:
- Если товар в топ‑3 SERP → высокая вероятность рекламы
- Наличие специального маркера в карточке товара
- Сравнение позиций в органике и рекламе
Морфологическая обработка для русского языка
Сравнение подходов
| Подход | Скорость | Точность | Рекомендация |
|---|---|---|---|
| pymorphy2 | Средняя | Высокая | Для точного нормализации |
| Токенизация + шинглы | Быстрая | Средняя | Для мгновенных ответов |
| Комбинированный | Оптимальная | Высокая | Для продакшена |
Оптимальное решение
class RussianMorphology {
constructor() {
// Для быстрой токенизации в памяти
this.tokenizer = new WordTokenizer();
// Для точной нормализации - вызов внешнего сервиса
this.pymorphy2Service = 'https://morphos.io/analyze';
}
async processQuery(query) {
// Быстрый путь для мгновенных ответов
const fastTokens = this.tokenizer.tokenize(query);
const ngrams = this.generateNgrams(fastTokens, 2, 3);
// Точный путь для индексации
const normalized = await this.normalizeWithPymorphy2(query);
return {
fast: ngrams,
accurate: normalized,
combined: this.combineResults(ngrams, normalized)
};
}
}
Антибот‑защита и производительность
Оптимальные параметры троттлинга
const bottleneckConfig = {
minTimeout: 1000, // 1 секунда между запросами
maxTimeout: 5000, // 5 секунд при ошибках
randomisationFactor: 0.3,
retryAttempts: 3
};
// Пример использования
const limiter = new Bottleneck(bottleneckConfig);
const throttledFetch = limiter.wrap(axios.get);
Рекомендуемые RPS/хост
Исходя из практики:
- Оптимальный RPS: 0.3‑0.5 запросов в секунду на хост
- Максимальный безопасный RPS: 0.8 запросов в секунду
- Backoff стратегия: экспоненциальный с джиттером
Использование прокси
const proxyList = [
'http://proxy1.example.com:8080',
'http://proxy2.example.com:8080',
// ...
];
class SmartProxyRotator {
constructor() {
this.proxies = proxyList;
this.currentIndex = 0;
}
getNextProxy() {
const proxy = this.proxies[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.proxies.length;
return proxy;
}
async makeRequest(url, options = {}) {
const proxy = this.getNextProxy();
const axiosConfig = {
...options,
proxy: {
host: proxy,
port: 8080
},
timeout: 10000
};
return axios.get(url, axiosConfig);
}
}
Архитектура очередей и кэширования
Гибридный подход с BullMQ
import Queue from 'bullmq';
import { OpenSearchClient } from './opensearch';
class WildberriesBotArchitecture {
constructor() {
// Очереди с приоритетом
this.instantQueue = new Queue('instant', {
connection: redisConfig,
defaultJobOptions: {
removeOnComplete: 100,
removeOnFail: 50
}
});
this.backgroundQueue = new Queue('background', {
connection: redisConfig,
defaultJobOptions: {
priority: 1,
delay: 5000
}
});
this.searchIndex = new OpenSearchClient();
}
async handleUserRequest(nmId) {
// 1. Проверяем мгновенный ответ из кэша/индекса
const instantResult = await this.getInstantResponse(nmId);
if (instantResult) {
return instantResult;
}
// 2. Ставим задачу в очередь с высоким приоритетом
const job = await this.instantQueue.add('process', {
nmId,
userId: context.userId
}, {
priority: 10,
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
});
// 3. Возвращаем немедленный ответ с ожиданием
return {
status: 'processing',
estimatedTime: 3000,
data: await this.getPartialResults(nmId)
};
}
async backgroundProcessing() {
// Фоновая обработка без приоритета
await this.backgroundQueue.process('deepAnalysis', async (job) => {
const { nmId } = job.data;
await this.performDeepAnalysis(nmId);
});
}
}
Структура данных в Redis/TTL кэше
const cacheStructure = {
suggest: {
ttl: 86400, // 24 часа
prefix: 'wb:suggest:'
},
serp: {
ttl: 3600, // 1 час
prefix: 'wb:serp:'
},
card: {
ttl: 172800, // 2 дня
prefix: 'wb:card:'
},
ads: {
ttl: 1800, // 30 минут
prefix: 'wb:ads:'
}
};
class CacheManager {
async getOrSet(key, fetchFunction, ttl) {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await fetchFunction();
await redis.setex(key, ttl, JSON.stringify(data));
return data;
}
}
Витрина индекса для мгновенных ответов
{
"mappings": {
"properties": {
"nmId": { "type": "keyword" },
"phrases": {
"type": "text",
"analyzer": "russian_shingle",
"fields": {
"keyword": { "type": "keyword" },
"edge": { "type": "text", "analyzer": "russian_edge_ngram" }
}
},
"positions": {
"type": "object",
"properties": {
"organic": { "type": "integer" },
"advertised": { "type": "boolean" },
"lastUpdated": { "type": "date" }
}
},
"scores": {
"type": "object",
"properties": {
"frequency": { "type": "float" },
"consistency": { "type": "float" },
"trend": { "type": "float" }
}
}
}
}
}
Источники
- Wildberries API Documentation – Product cards list
- Wildberries Seller API – Feedbacks and structure
- GitHub – Wildberries Scraper Bot Implementation
- OpenSearch Russian Language Analysis
- Telegram Bot API Documentation
- BullMQ Queue Management System
- Wildberries GitHub Topics – Parser Implementations
Заключение
Для увеличения охвата и ускорения работы вашего Wildberries‑бота рекомендуется:
- Внедрить гибридную архитектуру с мгновенными ответами из OpenSearch и фоновым глубоким сбором данных.
- Использовать комбинированный подход к морфологии: быстрая токенизация + шинглы для мгновенных ответов и pymorphy2 для точной нормализации.
- Оптимизировать троттлинг на уровне 0.3‑0.5 RPS/хост с экспоненциальным бэкоффом.
- Реализовать систему очередей BullMQ с приоритизацией запросов.
- Использовать ротацию прокси‑серверов для распределения нагрузки.
- Настроить многоуровневое кэширование с разными TTL для разных типов данных.
Такая архитектура позволит конкурентоспособно работать с охватом фраз и обеспечивать мгновенные ответы пользователям при соблюдении ограничений Wildberries.