Ozon Scraper

Быстро прокручивает страницу категории Ozon и копирует результаты в буфер обмена.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Advertisement:

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

Advertisement:

// ==UserScript==
// @name         Ozon Scraper
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Быстро прокручивает страницу категории Ozon и копирует результаты в буфер обмена.
// @author       torch
// @match        https://www.ozon.ru/*
// @grant        GM_setClipboard
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // Создание контейнера для панели управления
    const panel = document.createElement('div');
    panel.id = 'ozon-scraper-panel';
    panel.style.cssText = `
        position: fixed;
        bottom: 24px;
        right: 24px;
        z-index: 999999;
        background: rgba(20, 20, 20, 0.85);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        border: 1px solid rgba(255, 255, 255, 0.12);
        border-radius: 16px;
        padding: 16px;
        color: #f3f4f6;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
        width: 280px;
        box-sizing: border-box;
        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    `;

    panel.innerHTML = `
        <div style="font-weight: 600; margin-bottom: 12px; font-size: 14px; display: flex; justify-content: space-between; align-items: center; letter-spacing: -0.01em;">
            <span>Ozon Scraper (Fast)</span>
            <span id="scraper-status" style="font-size: 10px; background: #ef4444; color: #fff; padding: 3px 8px; border-radius: 20px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;">Остановлен</span>
        </div>
        <div style="font-size: 12px; margin-bottom: 16px; color: #9ca3af; display: flex; justify-content: space-between;">
            <span>Найдено товаров:</span>
            <span id="product-count" style="font-weight: 700; color: #10b981; font-size: 13px;">0</span>
        </div>
        <div style="display: flex; flex-direction: column; gap: 8px;">
            <button id="btn-toggle-scroll" style="background: #2563eb; color: white; border: none; padding: 10px; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 13px; transition: background 0.2s;">▶ Быстрая прокрутка</button>
            <button id="btn-copy-data" style="background: #10b981; color: #042f1a; border: none; padding: 10px; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 13px; transition: background 0.2s;">📋 Скопировать данные</button>
            <button id="btn-reset" style="background: transparent; color: #9ca3af; border: 1px solid #4b5563; padding: 8px; border-radius: 8px; cursor: pointer; font-size: 11px; transition: all 0.2s;">Сбросить счетчик</button>
        </div>
    `;
    document.body.appendChild(panel);

    // Переменные состояния
    let isScrolling = false;
    let scrollTimer = null;
    let lastCollectedCount = 0;
    let noNewItemsStreak = 0;
    const seenSkus = new Set();
    const collectedProducts = [];

    // Элементы интерфейса
    const statusBadge = document.getElementById('scraper-status');
    const countSpan = document.getElementById('product-count');
    const toggleBtn = document.getElementById('btn-toggle-scroll');
    const copyBtn = document.getElementById('btn-copy-data');
    const resetBtn = document.getElementById('btn-reset');

    // Функция парсинга карточек товаров
    function scanVisibleProducts() {
        const productLinks = document.querySelectorAll('a[href*="/product/"]');

        productLinks.forEach(link => {
            const href = link.getAttribute('href');
            if (!href) return;

            const skuMatch = href.match(/-(\d+)\/?(?:\?|$)/);
            const sku = skuMatch ? skuMatch[1] : href.split('?')[0];

            if (seenSkus.has(sku)) return;

            let cardContainer = link.closest('[data-index]') || link.closest('.tile-root') || link.parentElement;
            while (cardContainer && cardContainer !== document.body && !cardContainer.querySelector('img')) {
                cardContainer = cardContainer.parentElement;
            }

            if (!cardContainer) return;

            let name = "";
            const nameEl = cardContainer.querySelector('span[class*="tsBody500Medium"], a[href*="/product/"] span, span[class*="tsBody"]');
            if (nameEl) name = nameEl.innerText.trim();
            if (!name || name.length < 5) {
                name = link.innerText.trim();
            }

            if (!name || name.length < 4 || name.includes("₽") || name.includes("рейтинг")) return;

            let price = "N/A";
            const priceElements = Array.from(cardContainer.querySelectorAll('*')).filter(el => {
                return el.children.length === 0 && el.innerText && (el.innerText.includes('₽') || el.innerText.includes(' ₽'));
            });
            if (priceElements.length > 0) {
                price = priceElements[0].innerText.trim().replace(/\s+/g, ' ');
            }

            const cleanUrl = 'https://www.ozon.ru' + href.split('?')[0];
            seenSkus.add(sku);
            collectedProducts.push({ name, price, url: cleanUrl, sku });
        });

        countSpan.innerText = collectedProducts.length;
    }

    // Функция быстрой циклической прокрутки
    function scrollPageStep() {
        window.scrollBy(0, window.innerHeight * 0.85);
        scanVisibleProducts();

        if (collectedProducts.length === lastCollectedCount) {
            noNewItemsStreak++;
        } else {
            noNewItemsStreak = 0;
            lastCollectedCount = collectedProducts.length;
        }

        // Порог увеличен до 35 попыток (около 12 секунд ожидания при медленном интернете),
        // чтобы дать Ozon время на загрузку данных на высокой скорости прокрутки.
        if (noNewItemsStreak >= 35) {
            pauseScrolling();
            statusBadge.innerText = 'Завершено';
            statusBadge.style.background = '#10b981';
            statusBadge.style.color = '#fff';
            alert(`Сбор завершен. Уникальных товаров собрано: ${collectedProducts.length}`);
        }
    }

    function startScrolling() {
        isScrolling = true;
        toggleBtn.innerText = '⏸ Приостановить';
        toggleBtn.style.background = '#ef4444';
        statusBadge.innerText = 'Быстрый скролл';
        statusBadge.style.background = '#2563eb';

        // Быстрый интервал — 350 мс
        scrollTimer = setInterval(scrollPageStep, 350);
    }

    function pauseScrolling() {
        isScrolling = false;
        toggleBtn.innerText = '▶ Быстрая прокрутка';
        toggleBtn.style.background = '#2563eb';
        statusBadge.innerText = 'Остановлен';
        statusBadge.style.background = '#ef4444';
        if (scrollTimer) {
            clearInterval(scrollTimer);
            scrollTimer = null;
        }
    }

    // Обработчики кнопок
    toggleBtn.addEventListener('click', () => {
        if (isScrolling) {
            pauseScrolling();
        } else {
            startScrolling();
        }
    });

    copyBtn.addEventListener('click', () => {
        if (collectedProducts.length === 0) {
            alert("Нет данных для копирования. Пожалуйста, запустите прокрутку.");
            return;
        }

        let tsvOutput = "Название товара\tЦена\tСсылка на товар\tSKU\n";
        collectedProducts.forEach(p => {
            tsvOutput += `${p.name}\t${p.price}\t${p.url}\t${p.sku}\n`;
        });

        if (typeof GM_setClipboard !== 'undefined') {
            GM_setClipboard(tsvOutput);
            alert(`Данные (${collectedProducts.length} шт.) успешно скопированы! Откройте Excel или Google Таблицы и вставьте их через Ctrl+V.`);
        } else {
            navigator.clipboard.writeText(tsvOutput).then(() => {
                alert(`Данные (${collectedProducts.length} шт.) успешно скопированы!`);
            }).catch(err => {
                const fallbackArea = document.createElement('textarea');
                fallbackArea.value = tsvOutput;
                document.body.appendChild(fallbackArea);
                fallbackArea.select();
                document.execCommand('copy');
                document.body.removeChild(fallbackArea);
                alert(`Данные (${collectedProducts.length} шт.) скопированы (резервный метод).`);
            });
        }
    });

    resetBtn.addEventListener('click', () => {
        if (confirm("Вы действительно хотите очистить результаты?")) {
            seenSkus.clear();
            collectedProducts.length = 0;
            lastCollectedCount = 0;
            noNewItemsStreak = 0;
            countSpan.innerText = '0';
            if (!isScrolling) {
                statusBadge.innerText = 'Остановлен';
                statusBadge.style.background = '#ef4444';
            }
        }
    });

    scanVisibleProducts();
})();