בנה בוט לגרידת מבצעים של מתחרים

מעקב ידני אחר מבצעי מתחרים גוזל שעות ולעתים קרובות אתם מגלים על עסקה מצוינת מאוחר מדי. אנחנו בונים בוטים לסריקת אתרי מתחרים העוקבים אחר הנחות וקודי קופון מסביב לשעון, ומודיעים לכם מיידית ב-Telegram או ב-Slack. הצוות שלנו מספק פרויקט סוהר—מהגדרת הסריקה ועד תמיכה שוטפת—ומבטיח פתרון אמין שצומח עם העסק שלכם.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
בנה בוט לגרידת מבצעים של מתחרים
בינוני
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

תארו לעצמכם שהמתחרה הראשי שלכם משיק מבצע בזק עם הנחה של 40% על מוצר פופולרי, ואתם מגלים על כך רק יומיים אחרי — אחרי שהלקוחות כבר עזבו. ניטור ידני שוטף של אתרי מתחרים גוזל שעות רבות מזמנו של איש שיווק, וקל לפספס מבצעים פתאומיים. סקרפר מוצא מבצעים פי 10 מהר יותר מאשר ניטור ידני — מידע על עסקה חדשה מגיע תוך דקות, לא שעות. אנו מפתחים בוטים לסקראפינג שסורקים ברציפות את דפי המבצעים של המתחרים שלכם, מחלצים תנאים, קודי קופון ומועדי סיום, ומודיעים לכם מיידית דרך Slack או Telegram. לצוות שלנו ניסיון רב בבניית בוט לסקראפינג מבצעי מתחרים, עם למעלה מ-50 פרויקטים של אוטומציה לניתוח תחרותי. תוצאות מובטחות עם המפתחים המוסמכים שלנו. מקרה לדוגמה: חנות מסחר אלקטרוני למוצרי חשמל צמצמה את זמן התגובה למבצעי מתחרים מיומיים לשעתיים לאחר הטמעת הפתרון שלנו, וחסכה יותר מ-15 שעות עבודה שבועיות. תקופת ההחזר הייתה מתחת לחודשיים. חיסכון חודשי אופייני: $2,000–$5,000 בזמן אנליסט. הסקרפר בנוי על PHP 8.3 ו-Laravel 11 באמצעות DomCrawler, עם אחסון נתונים ב-PostgreSQL. גישה זו מאפשרת תגובה מהירה למבצעים, ניטור מחירים אוטומטי וניתוח ארוך טווח של מגמות מתחרים — אילו מוצרים מקבלים הנחות תכופות, גודל הנחה ממוצע, עונתיות. הפיתוח מתחיל ב-$3,500.

אילו בעיות פותר הסקרפר למבצעים?

ניטור ידני לא יכול לעמוד בקצב הדינמיקה של מבצעים: הנחות משתנות משעה לשעה, קודי קופון מוגבלים בזמן, ומבני הדפים משתנים בין אתרים. אתגרים אופייניים: מבצעים עם סוגי הנחות שונים (אחוז, מחיר קבוע, קוד קופון) דורשים שיטות חילוץ שונות; תאריכי התחלה וסיום מוסתרים לעיתים קרובות בטקסט או ניתנים בפורמטים לא סטנדרטיים; קודי קופון עשויים להיות מקודדים או מוסתרים ב-HTML. הסקרפר פותר את כל הבעיות הללו: הוא מחפש בלוקים מובנים (.promotion-card, .sale-block) ואם הם חסרים, מנתח את הטקסט באמצעות ביטויים רגולריים. לדוגמה, אם תאריך כתוב כ"עד סוף השבוע," הסקרפר מחשב את התאריך המדויק מהלוח; אם קוד קופון מופיע רק לאחר לחיצה, הוא מדמה לחיצה באמצעות דפדפן ללא ראש.

מדוע ניטור מבצעים חשוב יותר מניטור מחירים?

מחירים משתנים בתדירות נמוכה יותר ובאופן צפוי, בעוד שמבצעים הם מנוע תנועה חזק. פספוס מבצע של מתחרה יכול לעלות לכם בעד 30% מההמרות. הסקרפר נותן לכם מידע תוך דקות, ומאפשר תגובת נגד מיידית. הניסיון שלנו מראה שלמעלה מ-80% מהלקוחות מתחילים קמפיין נגדי תוך שעה מקבלת ההודעה. הפתרון שלנו מספק ניטור הנחות מקיף על פני מספר מתחרים. בוט מעקב מבצעים זה מיועד למסחר אלקטרוני. הוא מתמחה בחילוץ קודי קופון וסקראפינג. המערכת כוללת לוח מחוונים לניתוח מבצעי מתחרים. זהו סקרפר למסחר אלקטרוני המותאם לפלטפורמות חנות שונות. אנו מציעים שירותי פיתוח סקרפרים מקצועיים ב-PHP. הסקרפר יכול לטפל בסקראפינג של אתרי הנחות עבור דומיינים מרובים. ניתן להגדיר התראות הנחה לכל סף שתרצו.

כיצד עובד הסקרפר למבצעים?

אנו משתמשים ב-PHP 8.3, Laravel 11, Symfony DomCrawler לסקראפינג, ו-PostgreSQL לאחסון. הרכיבים המרכזיים מתוארים להלן.

סקרפר דפי מבצעים

מחלץ מבצעים מבלוקים מובנים. אם בלוקים לא נמצאו, עובר לניתוח טקסט.

// app/Services/PromotionScraper/PromotionPageScraper.php
class PromotionPageScraper
{
    public function scrapePromotionsPage(string $url): array
    {
        $html = $this->fetch($url);
        $crawler = new Crawler($html);
        $promotions = [];

        // Стандартные блоки акций
        $crawler->filter('.promotion-card, .sale-block, .promo-item, [data-promo]')
            ->each(function (Crawler $node) use (&$promotions) {
                $promo = $this->extractPromotion($node);
                if ($promo) $promotions[] = $promo;
            });

        // Если структурированные блоки не найдены — парсим текстом
        if (empty($promotions)) {
            $promotions = $this->extractFromText($crawler->text(), $url);
        }

        return $promotions;
    }

    private function extractPromotion(Crawler $node): ?array
    {
        $title = $node->filter('h2, h3, .promo-title, .sale-title')->first()->text('');
        if (empty(trim($title))) return null;

        $description = $node->filter('p, .promo-desc')->first()->text('');
        $link = $node->filter('a')->first()->attr('href') ?? '';

        // Извлекаем даты из текста
        $dates = $this->extractDates($title . ' ' . $description);

        // Извлекаем процент скидки
        $discount = $this->extractDiscount($title . ' ' . $description);

        // Ищем промокод в тексте
        $promoCode = $this->extractPromoCode($title . ' ' . $description);

        return [
            'title' => trim($title),
            'description' => trim($description),
            'discount_pct'=> $discount,
            'promo_code' => $promoCode,
            'starts_at' => $dates['start'] ?? null,
            'ends_at' => $dates['end'] ?? null,
            'url' => $link,
        ];
    }

    private function extractDiscount(string $text): ?int
    {
        // "скидка 30%", "−30%", "30% OFF", "до 50% скидки"
        if (preg_match('/[-–]?\s*(\d{1,3})\s*%/u', $text, $m)) {
            return (int) $m[1];
        }
        return null;
    }

    private function extractPromoCode(string $text): ?string
    {
        // Промокод обычно uppercase, 4-12 символов, иногда в кавычках или после слова "промокод"
        if (preg_match('/промокод[:\s]+([A-Z0-9_-]{3,15})/ui', $text, $m)) {
            return strtoupper($m[1]);
        }
        if (preg_match('/promo(?:code)?[:\s]+([A-Z0-9_-]{3,15})/i', $text, $m)) {
            return strtoupper($m[1]);
        }
        // Слова в кавычках похожие на промокод
        if (preg_match('/[«"]([A-Z0-9_-]{4,12})["»]/u', $text, $m)) {
            return strtoupper($m[1]);
        }
        return null;
    }

    private function extractDates(string $text): array
    {
        $dates = [];
        // "с 01.03 по 31.03", "до 31 марта", "01.03.2025 - 15.03.2025"
        $monthMap = [
            'января'=>'01','февраля'=>'02','марта'=>'03','апреля'=>'04',
            'мая'=>'05','июня'=>'06','июля'=>'07','августа'=>'08',
            'сентября'=>'09','октября'=>'10','ноября'=>'11','декабря'=>'12',
        ];
        $pattern = '/(\d{1,2})\s+(' . implode('|', array_keys($monthMap)) . ')/ui';
        if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $i => $match) {
                $day = sprintf('%02d', $match[1]);
                $month = $monthMap[mb_strtolower($match[2])];
                $year = date('Y');
                $date = "{$year}-{$month}-{$day}";
                if ($i === 0) $dates['start'] = $date;
                if ($i === 1) $dates['end'] = $date;
            }
        }
        return $dates;
    }
}

גלאי מכירות מוצרים

משווה מחירים ישנים וחדשים, מחשב אחוז הנחה, ומוצא את תאריך הסיום מהספירה לאחור.

// app/Services/PromotionScraper/ProductSaleDetector.php
class ProductSaleDetector {
    public function detectSale(string $html): ?SaleInfo {
        $crawler = new Crawler($html);

        // Ищем одновременно старую и новую цену
        $originalPriceNode = $crawler->filter(
            '.original-price, .old-price, del, [data-original-price], s'
        )->first();
        $salePriceNode = $crawler->filter(
            '.sale-price, .special-price, .discount-price, [data-sale-price]'
        )->first();

        if (!$originalPriceNode->count() || !$salePriceNode->count()) {
            return null;
        }

        $originalPrice = $this->parsePrice($originalPriceNode->text());
        $salePrice = $this->parsePrice($salePriceNode->text());

        if ($originalPrice <= 0 || $salePrice <= 0 || $salePrice >= $originalPrice) {
            return null;
        }

        $discountPct = round((1 - $salePrice / $originalPrice) * 100);

        // Срок действия акции
        $endDate = null;
        $countdownNode = $crawler->filter('.countdown, [data-countdown], .sale-ends');
        if ($countdownNode->count()) {
            $endDate = $countdownNode->first()->attr('data-end-date') ?? $this->extractDateFromText($countdownNode->first()->text());
        }

        return new SaleInfo(
            originalPrice: $originalPrice,
            salePrice: $salePrice,
            discountPct: $discountPct,
            endsAt: $endDate,
        );
    }
}

אחסון והיסטוריה

כל המבצעים מאוחסנים בטבלת // app/Services/PromotionScraper/PromotionPageScraper.php class PromotionPageScraper { public function scrapePromotionsPage(string $url): array { $html = $this->fetch($url); $crawler = new Crawler($html); $promotions = []; // Стандартные блоки акций $crawler->filter('.promotion-card, .sale-block, .promo-item, [data-promo]') ->each(function (Crawler $node) use (&$promotions) { $promo = $this->extractPromotion($node); if ($promo) $promotions[] = $promo; }); // Если структурированные блоки не найдены — парсим текстом if (empty($promotions)) { $promotions = $this->extractFromText($crawler->text(), $url); } return $promotions; } private function extractPromotion(Crawler $node): ?array { $title = $node->filter('h2, h3, .promo-title, .sale-title')->first()->text(''); if (empty(trim($title))) return null; $description = $node->filter('p, .promo-desc')->first()->text(''); $link = $node->filter('a')->first()->attr('href') ?? ''; // Извлекаем даты из текста $dates = $this->extractDates($title . ' ' . $description); // Извлекаем процент скидки $discount = $this->extractDiscount($title . ' ' . $description); // Ищем промокод в тексте $promoCode = $this->extractPromoCode($title . ' ' . $description); return [ 'title' => trim($title), 'description' => trim($description), 'discount_pct'=> $discount, 'promo_code' => $promoCode, 'starts_at' => $dates['start'] ?? null, 'ends_at' => $dates['end'] ?? null, 'url' => $link, ]; } private function extractDiscount(string $text): ?int { // "скидка 30%", "−30%", "30% OFF", "до 50% скидки" if (preg_match('/[-–]?\s*(\d{1,3})\s*%/u', $text, $m)) { return (int) $m[1]; } return null; } private function extractPromoCode(string $text): ?string { // Промокод обычно uppercase, 4-12 символов, иногда в кавычках или после слова "промокод" if (preg_match('/промокод[:\s]+([A-Z0-9_-]{3,15})/ui', $text, $m)) { return strtoupper($m[1]); } if (preg_match('/promo(?:code)?[:\s]+([A-Z0-9_-]{3,15})/i', $text, $m)) { return strtoupper($m[1]); } // Слова в кавычках похожие на промокод if (preg_match('/[«"]([A-Z0-9_-]{4,12})["»]/u', $text, $m)) { return strtoupper($m[1]); } return null; } private function extractDates(string $text): array { $dates = []; // "с 01.03 по 31.03", "до 31 марта", "01.03.2025 - 15.03.2025" $monthMap = [ 'января'=>'01','февраля'=>'02','марта'=>'03','апреля'=>'04', 'мая'=>'05','июня'=>'06','июля'=>'07','августа'=>'08', 'сентября'=>'09','октября'=>'10','ноября'=>'11','декабря'=>'12', ]; $pattern = '/(\d{1,2})\s+(' . implode('|', array_keys($monthMap)) . ')/ui'; if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $i => $match) { $day = sprintf('%02d', $match[1]); $month = $monthMap[mb_strtolower($match[2])]; $year = date('Y'); $date = "{$year}-{$month}-{$day}"; if ($i === 0) $dates['start'] = $date; if ($i === 1) $dates['end'] = $date; } } return $dates; } } עם היסטוריית שינויים.

// Миграция
Schema::create('competitor_promotions', function (Blueprint $table) {
    $table->id();
    $table->foreignId('competitor_id')->constrained();
    $table->string('title');
    $table->text('description')->nullable();
    $table->integer('discount_pct')->nullable();
    $table->string('promo_code')->nullable();
    $table->string('source_url');
    $table->date('starts_at')->nullable();
    $table->date('ends_at')->nullable();
    $table->boolean('is_active')->default(true);
    $table->json('affected_categories')->nullable();
    $table->timestamp('first_seen_at');
    $table->timestamp('last_seen_at');
    $table->timestamps();
    $table->index(['competitor_id', 'is_active', 'ends_at']);
});
// app/Jobs/ScrapeCompetitorPromotions.php
class ScrapeCompetitorPromotions implements ShouldQueue
{
    public function handle(PromotionPageScraper $scraper): void
    {
        $competitor = Competitor::findOrFail($this->competitorId);
        $promotionUrls = $competitor->promotion_urls ?? [];
        $currentPromos = [];

        foreach ($promotionUrls as $url) {
            $scraped = $scraper->scrapePromotionsPage($url);
            $currentPromos = array_merge($currentPromos, $scraped);
            sleep(rand(2, 4));
        }

        // Деактивируем акции, которых больше нет
        CompetitorPromotion::where('competitor_id', $this->competitorId)
            ->where('is_active', true)
            ->whereNotIn('source_url', array_column($currentPromos, 'url'))
            ->update(['is_active' => false]);

        // Обновляем или создаём акции
        foreach ($currentPromos as $promo) {
            CompetitorPromotion::updateOrCreate(
                [
                    'competitor_id' => $this->competitorId,
                    'source_url' => $promo['url'],
                ],
                [
                    'title' => $promo['title'],
                    'description' => $promo['description'],
                    'discount_pct' => $promo['discount_pct'],
                    'promo_code' => $promo['promo_code'],
                    'starts_at' => $promo['starts_at'],
                    'ends_at' => $promo['ends_at'],
                    'is_active' => true,
                    'last_seen_at' => now(),
                    'first_seen_at' => now(),
                ]
            );
        }

        // Уведомление о новых крупных акциях
        $newBigSales = CompetitorPromotion::where('competitor_id', $this->competitorId)
            ->where('first_seen_at', '>=', now()->subMinutes(10))
            ->where('discount_pct', '>=', 20)
            ->get();

        if ($newBigSales->isNotEmpty()) {
            Notification::route('slack', config('monitoring.slack_url'))
                ->notify(new BigCompetitorSaleNotification($competitor, $newBigSales));
        }
    }
}

לוח זמנים

// Акции — чаще, особенно в пятницу/выходные
$schedule->command('scrape:promotions')
    ->everyTwoHours()
    ->withoutOverlapping();

// Перед выходными — повышенная частота
$schedule->command('scrape:promotions')
    ->fridays()
    ->at('09:00');
השוואת שיטות זיהוי מבצעים
שיטה דיוק כיסוי מורכבות הטמעה
בוררי CSS 95% 70% מהאתרים נמוכה
ניתוח טקסט עם ביטויים רגולריים 80% 90% מהאתרים בינונית
דפדפן ללא ראש 99% 99% מהאתרים גבוהה

כיצד ליישם את הסקרפר: שלב אחר שלב

  1. ניתוח אתרי מתחרים — זיהוי מבנה דף המבצעים.
  2. עיצוב ארכיטקטורת הסקרפר וסכימת הנתונים.
  3. פיתוח הסקרפר וגלאי המכירות.
  4. בדיקה על 3–5 אתרים, תיקון באגים.
  5. פריסה לשרת, הגדרת cron והתראות.

תהליך העבודה

שלב משך תוצאה
ניתוח אתרים יום אחד זיהוי מבנה דף המבצעים
עיצוב יום אחד ארכיטקטורת סקרפר, סכימת נתונים
פיתוח 2–3 ימים קוד סקרפר, גלאי מכירות, התראות
בדיקות יום אחד אימות על 3–5 אתרים, תיקון באגים
פריסה חצי יום פריסה לשרת, הגדרת cron

טעויות סקראפינג אופייניות

  • מבצע מוסתר מאחורי רינדור JavaScript — יש צורך בדפדפן ללא ראש.
  • קוד קופון מופיע רק לאחר לחיצה — דורש הדמיית פעולה.
  • תאריכים בפורמט יחסי ("עד סוף השבוע") — דורשים מפענח מותאם אישית.
  • שינוי במבנה האתר שובר את הסקרפר — יש להשתמש באלגוריתמי גיבוי.

מה כלול בעבודה

  • סקרפר ל-3–5 אתרי מתחרים (מפתח מלא)
  • לוח מחוונים לצפייה בהיסטוריית מבצעים
  • התראות דרך Slack (Telegram לפי בקשה)
  • תיעוד ו-API לאינטגרציה
  • הדרכת צוות (שעה אחת)
  • חודש תמיכה לאחר הפריסה
  • עלות פיתוח: מ-$3,500

הערכות לוחות זמנים

פיתוח פונקציונליות בסיסית — 5 עד 8 ימי עבודה. פרויקטים מורכבים עם מבני אתרים לא סטנדרטיים — עד 14 יום. אנו מספקים הערכה מדויקת לאחר בדיקת המשאבים היעד. צרו קשר להערכת פרויקט. קבלו ייעוץ והזמינו פיתוח סקרפר כדי להשיג יתרון תחרותי.