Реалізація Smart-банерів (персоналізована реклама) на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Smart-банерів (персоналізована реклама) на сайті
Середня
~3-5 робочих днів
Часті питання

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

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація Smart-баннерів (персоналізована реклама) на веб-сайті

Smart-баннери — це рекламні блоки, вміст яких генерується динамічно на основі поведінки користувача, історії переглядів та даних з продуктового каталогу. На відміну від статичних баннерів, вони показують саме ті товари або послуги, які користувач уже переглядав або які алгоритм передбачає як релевантні.

Механіка роботи

Система складається з трьох частин: слідження за переглядами, рушій рекомендацій та рендеринг баннера. Дані про переглянуті товари накопичуються в браузері та/або на сервері, рушій ранжирує позиції, баннер збирається з шаблону.

Трекінг переглядів

class ViewHistoryTracker {
  private readonly KEY = 'view_history';
  private readonly MAX_ITEMS = 50;

  track(item: ViewedItem): void {
    const history = this.get();

    // Видаляємо дублюючийся елемент, додаємо на початок
    const filtered = history.filter(i => i.id !== item.id);
    const updated = [
      { ...item, viewed_at: Date.now() },
      ...filtered,
    ].slice(0, this.MAX_ITEMS);

    localStorage.setItem(this.KEY, JSON.stringify(updated));
    this.syncToServer(item); // асинхронно
  }

  get(): ViewedItem[] {
    try {
      return JSON.parse(localStorage.getItem(this.KEY) ?? '[]');
    } catch {
      return [];
    }
  }

  getRecent(count = 10): ViewedItem[] {
    return this.get().slice(0, count);
  }

  private async syncToServer(item: ViewedItem): Promise<void> {
    if (!getAuthToken()) return; // синхронізуємо тільки для авторизованих
    await fetch('/api/views', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(item),
    });
  }
}

// Приклад на сторінці товару
const tracker = new ViewHistoryTracker();
tracker.track({
  id: '123',
  type: 'product',
  category: 'laptops',
  price: 89900,
  title: 'MacBook Pro 14',
  image: '/images/mbp14.jpg',
  url: '/catalog/laptops/macbook-pro-14',
});

Рушій рекомендацій

Базовий алгоритм — коллаборативна фільтрація на основі історії переглядів з урахуванням ваг (нещодавні перегляди важать більше):

function rankItems(
  history: ViewedItem[],
  candidates: CatalogItem[]
): CatalogItem[] {
  const categoryWeights: Record<string, number> = {};
  const viewedIds = new Set(history.map(i => i.id));

  // Лічимо ваги категорій з історії переглядів
  history.forEach((item, index) => {
    const recencyWeight = 1 / (index + 1); // перші перегляди важливіші
    categoryWeights[item.category] = (categoryWeights[item.category] ?? 0) + recencyWeight;
  });

  return candidates
    .filter(c => !viewedIds.has(c.id)) // видаляємо уже переглянуті
    .map(candidate => ({
      ...candidate,
      score: (categoryWeights[candidate.category] ?? 0) * (candidate.popularity ?? 1),
    }))
    .sort((a, b) => b.score - a.score)
    .slice(0, 6);
}

Для серйозних проектів рушій переносимо на сервер — PHP/Python — та використовуємо матрицю користувач×товар.

Рендеринг Smart-баннера

interface SmartBannerProps {
  placement: 'sidebar' | 'inline' | 'sticky-bottom';
  title?: string;
}

function SmartBanner({ placement, title = 'Ви дивилися' }: SmartBannerProps) {
  const [items, setItems] = useState<CatalogItem[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const history = tracker.getRecent();
    if (history.length === 0) {
      setLoading(false);
      return;
    }

    fetch('/api/recommendations', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        viewed_ids: history.map(i => i.id),
        categories: [...new Set(history.map(i => i.category))],
        limit: placement === 'sidebar' ? 4 : 6,
      }),
    })
      .then(r => r.json())
      .then(data => setItems(data.items))
      .finally(() => setLoading(false));
  }, [placement]);

  if (loading) return <BannerSkeleton count={4} />;
  if (items.length === 0) return null; // не показуємо порожній баннер

  return (
    <div className={`smart-banner smart-banner--${placement}`}>
      <h3 className="smart-banner__title">{title}</h3>
      <div className="smart-banner__grid">
        {items.map(item => (
          <a
            key={item.id}
            href={item.url}
            className="smart-banner__item"
            onClick={() => trackBannerClick(item, placement)}
          >
            <img src={item.image} alt={item.title} loading="lazy" />
            <span className="smart-banner__name">{item.title}</span>
            <span className="smart-banner__price">{formatPrice(item.price)}</span>
          </a>
        ))}
      </div>
    </div>
  );
}

function trackBannerClick(item: CatalogItem, placement: string): void {
  gtag('event', 'smart_banner_click', {
    item_id: item.id,
    item_name: item.title,
    placement,
    item_category: item.category,
  });
}

Серверний ендпоінт рекомендацій

// RecommendationsController.php
class RecommendationsController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $viewedIds = $request->input('viewed_ids', []);
        $categories = $request->input('categories', []);
        $limit = min($request->input('limit', 6), 12);

        $items = Product::query()
            ->whereNotIn('id', $viewedIds)
            ->where('is_active', true)
            ->where(function ($q) use ($categories) {
                $q->whereIn('category_slug', $categories)
                  ->orWhere('is_bestseller', true);
            })
            ->orderByRaw('
                CASE WHEN category_slug = ANY(?) THEN 1 ELSE 2 END,
                popularity DESC
            ', ['{' . implode(',', $categories) . '}'])
            ->limit($limit)
            ->get(['id', 'title', 'price', 'image', 'url', 'category_slug']);

        return response()->json(['items' => $items]);
    }
}

Персоналізація через зовнішні платформи

Для e-commerce з великим каталогом (10k+ товарів) варто розглянути спеціалізовані рушії:

  • Retail Rocket — російський сервіс персоналізації, інтегрується через JS-піксель
  • Mindbox — CDP з модулем рекомендацій, API-інтеграція
  • Dynamic Yield — enterprise-рішення з ML-рекомендаціями

Базова інтеграція Retail Rocket:

// Трекінг перегляду товару
rrApi.view(123456); // ID товару в системі Retail Rocket

// Трекінг додавання в кошик
rrApi.addToBasket(123456);

// Блок рекомендацій рендерується через callback
rrApiOnReady(function() {
  rrApi.recommend('block_id_from_rr_panel', {
    callback: function(items) {
      renderRecommendations(items);
    }
  });
});

Графік

Власна реалізація трекінгу + рушій рекомендацій + баннер-компонент: 3-5 днів. Інтеграція з Retail Rocket або аналогом: 1-2 дні. Серверний рушій рекомендацій з матрицею на PostgreSQL: 3-5 днів.