Розробка бота-парсера наявності товарів у конкурентів
Дані про остатки конкурентів відкривають можливості: піднести ціну, коли у конкурента товар закінчився; сигналізувати менеджеру про поповнення запасів; показувати "Осталось мало" коли у конкурентів пусто. Бот мониторит наявність по розпорядженню та реагує на зміни.
Що відслідковується
- Наявність/відсутність — є ли товар у продажу
- Кількість — якщо сайт показує остаток ("Осталось 3 шт")
- Статус — в наявності / під замовлення / знято з виробництва / тимчасово немає
- Дати появи — коли товар повернувся в продаж
Екстрактор статусу наявності
class StockStatusExtractor
{
private array $inStockPatterns = ['/в\s*наличии/ui', '/есть/ui', '/доступен/ui'];
private array $outOfStockPatterns = ['/нет\s*в\s*наличии/ui', '/распродан/ui'];
public function extract(string $html, array $config = []): StockStatus
{
$crawler = new Crawler($html);
// Стратегія 1: Microdata (найнадійніша)
if ($availability = $this->extractFromMicrodata($crawler)) {
return $availability;
}
// Стратегія 2: JSON-LD
if ($availability = $this->extractFromJsonLd($crawler)) {
return $availability;
}
// Стратегія 3: Кастомні CSS-селектори з конфігу
if (!empty($config['stock_selector'])) {
if ($availability = $this->extractWithSelector($crawler, $config)) {
return $availability;
}
}
// Стратегія 4: Стан кнопки ("Купить" / "В корзину")
return $this->extractFromButtonState($crawler);
}
public function extractQuantity(string $html): ?int
{
$crawler = new Crawler($html);
$patterns = [
'/осталось\s+(\d+)\s*(шт|штук)/ui',
'/в\s+наличии[:\s]+(\d+)/ui',
];
$text = $crawler->text();
foreach ($patterns as $pattern) {
if (preg_match($pattern, $text, $m)) {
return (int) $m[1];
}
}
return null;
}
}
Модель та історія
class CompetitorStock extends Model
{
protected $casts = ['in_stock' => 'boolean'];
protected static function booted(): void
{
static::updated(function (self $model) {
if ($model->wasChanged('in_stock')) {
CompetitorStockChanged::dispatch($model);
}
});
}
}
Реакція на зміни
class HandleCompetitorStockChanged
{
public function handle(CompetitorStockChanged $event): void
{
$stock = $event->stock;
// Конкурент закінчив товар → уведомляємо про можливість поднять ціну
if (!$stock->in_stock && $stock->getOriginal('in_stock')) {
$this->notifyPriceOpportunity($stock->product, $stock->competitor);
}
// ВСІ конкуренти закінчили → автоматично піднять ціну
$allOutOfStock = CompetitorStock::where('product_id', $stock->product_id)
->where('in_stock', true)->doesntExist();
if ($allOutOfStock && config('repricing.raise_when_competitors_out')) {
$this->applyScarcityPricing($stock->product);
}
}
}
Строк розробки: моніторинг 3-5 конкурентів з історією та подіями — 5-7 робочих днів.







