Last active
December 4, 2025 08:30
-
-
Save qFamouse/baf5ee80a4630744c089f725c3c03a30 to your computer and use it in GitHub Desktop.
Копирование данных с карточки товара Ozon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Ozon Copy Data | |
| // @namespace https://github.com/qFamouse/ | |
| // @version 1.2 | |
| // @description Копирование данных с карточки товара Ozon | |
| // @author Famouse | |
| // @match https://ozon.ru/product/* | |
| // @match https://ozon.by/product/* | |
| // @match https://ozon.kz/product/* | |
| // @match https://ozon.uz/product/* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=ozon.by | |
| // @grant GM_setValue | |
| // @grant GM_getValue | |
| // @grant GM_registerMenuCommand | |
| // @updateURL https://gist.github.com/qFamouse/baf5ee80a4630744c089f725c3c03a30/raw/8b7552650b1c4596ace6258d5ec380c1beca8d31/ozon-copy-data.user.js | |
| // @downloadURL https://gist.github.com/qFamouse/baf5ee80a4630744c089f725c3c03a30/raw/8b7552650b1c4596ace6258d5ec380c1beca8d31/ozon-copy-data.user.js | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // Дефолтные настройки | |
| const DEFAULT_SETTINGS = { | |
| copyTitle: true, | |
| copyArticle: true, | |
| copyPrice: true, | |
| copyOldPrice: true, | |
| copyComposition: true, | |
| copyComplectation: true, | |
| copyUsage: true, | |
| copyCharacteristics: true | |
| }; | |
| // Загрузка настроек | |
| function loadSettings() { | |
| const saved = GM_getValue('ozon_copy_settings', null); | |
| return saved ? JSON.parse(saved) : DEFAULT_SETTINGS; | |
| } | |
| // Сохранение настроек | |
| function saveSettings(settings) { | |
| GM_setValue('ozon_copy_settings', JSON.stringify(settings)); | |
| } | |
| // Работа с накопительным буфером | |
| function getAccumulatedData() { | |
| const data = GM_getValue('ozon_accumulated_buffer', ''); | |
| return data; | |
| } | |
| function addToAccumulatedData(newData) { | |
| const current = getAccumulatedData(); | |
| // Проверка на дубликат | |
| if (current) { | |
| // Пытаемся найти артикул в новых данных | |
| const newArticleMatch = newData.match(/Артикул:\s*(\d+)/); | |
| if (newArticleMatch) { | |
| // Если артикул есть — ищем его в накопленных данных | |
| const articleRegex = new RegExp(`Артикул:\\s*${newArticleMatch[1]}\\b`); | |
| if (articleRegex.test(current)) { | |
| return { isDuplicate: true, data: current }; | |
| } | |
| } else { | |
| // Если артикула нет — сравниваем по полному содержимому | |
| const entries = current.split('\n\n' + '='.repeat(60) + '\n\n'); | |
| if (entries.some(entry => entry.trim() === newData.trim())) { | |
| return { isDuplicate: true, data: current }; | |
| } | |
| } | |
| } | |
| // Не дубликат — добавляем | |
| const separator = current ? '\n\n' + '='.repeat(60) + '\n\n' : ''; | |
| const updated = current + separator + newData; | |
| GM_setValue('ozon_accumulated_buffer', updated); | |
| return { isDuplicate: false, data: updated }; | |
| } | |
| function clearAccumulatedData() { | |
| GM_setValue('ozon_accumulated_buffer', ''); | |
| } | |
| function getAccumulatedCount() { | |
| const data = getAccumulatedData(); | |
| if (!data) return 0; | |
| return (data.match(/Название:/g) || []).length; | |
| } | |
| let settings = loadSettings(); | |
| // Функция извлечения данных | |
| function extractData() { | |
| const data = {}; | |
| // Название (более надёжный селектор) | |
| if (settings.copyTitle) { | |
| const titleEl = document.querySelector('h1[class*="tsHeadline"]') || | |
| document.querySelector('[data-widget="webProductHeading"] h1'); | |
| data.title = titleEl?.textContent.trim() || 'Не найдено'; | |
| } | |
| // Артикул | |
| if (settings.copyArticle) { | |
| const articleEl = Array.from(document.querySelectorAll('button, div')) | |
| .find(el => el.textContent.includes('Артикул:')); | |
| const match = articleEl?.textContent.match(/Артикул:\s*(\d+)/); | |
| data.article = match ? match[1] : 'Не найден'; | |
| } | |
| // Текущая цена | |
| if (settings.copyPrice) { | |
| const priceEl = document.querySelector('[class*="tsHeadline600Large"]') || | |
| document.querySelector('[data-widget="webPrice"] span'); | |
| data.price = priceEl?.textContent.trim() || 'Не найдена'; | |
| } | |
| // Старая цена | |
| if (settings.copyOldPrice) { | |
| const oldPriceEl = document.querySelector('[class*="pdp_bf7"]') || | |
| Array.from(document.querySelectorAll('span')) | |
| .find(el => el.style.textDecoration === 'line-through'); | |
| data.oldPrice = oldPriceEl?.textContent.trim() || 'Нет'; | |
| } | |
| // Состав | |
| if (settings.copyComposition) { | |
| const compositionEl = Array.from(document.querySelectorAll('h3')) | |
| .find(h => h.textContent.trim() === 'Состав') | |
| ?.nextElementSibling; | |
| data.composition = compositionEl?.textContent.trim() || 'Не найден'; | |
| } | |
| // Комплектация | |
| if (settings.copyComplectation) { | |
| const complectationEl = Array.from(document.querySelectorAll('h3')) | |
| .find(h => h.textContent.trim() === 'Комплектация') | |
| ?.nextElementSibling; | |
| data.complectation = complectationEl?.textContent.trim() || 'Не найдена'; | |
| } | |
| // Способ применения | |
| if (settings.copyUsage) { | |
| const usageEl = Array.from(document.querySelectorAll('h3')) | |
| .find(h => h.textContent.trim() === 'Способ применения') | |
| ?.nextElementSibling; | |
| data.usage = usageEl?.textContent.trim() || 'Не найден'; | |
| } | |
| // Характеристики | |
| if (settings.copyCharacteristics) { | |
| const chars = {}; | |
| const charSection = document.querySelector('#section-characteristics, [id*="characteristics"]'); | |
| if (charSection) { | |
| const items = charSection.querySelectorAll('dl'); | |
| items.forEach(dl => { | |
| const key = dl.querySelector('dt')?.textContent.trim(); | |
| const value = dl.querySelector('dd')?.textContent.trim(); | |
| if (key && value) { | |
| chars[key] = value; | |
| } | |
| }); | |
| } | |
| data.characteristics = Object.keys(chars).length > 0 ? chars : 'Не найдены'; | |
| } | |
| return data; | |
| } | |
| // Форматирование для копирования | |
| function formatData(data) { | |
| let result = []; | |
| if (data.title) result.push(`Название: ${data.title}`); | |
| if (data.article) result.push(`Артикул: ${data.article}`); | |
| if (data.price) result.push(`Цена: ${data.price}`); | |
| if (data.oldPrice && data.oldPrice !== 'Нет') result.push(`Старая цена: ${data.oldPrice}`); | |
| if (data.composition && data.composition !== 'Не найден') result.push(`\nСостав:\n${data.composition}`); | |
| if (data.complectation && data.complectation !== 'Не найдена') result.push(`\nКомплектация:\n${data.complectation}`); | |
| if (data.usage && data.usage !== 'Не найден') result.push(`\nСпособ применения:\n${data.usage}`); | |
| if (data.characteristics && data.characteristics !== 'Не найдены') { | |
| result.push('\nХарактеристики:'); | |
| for (const [key, value] of Object.entries(data.characteristics)) { | |
| result.push(` ${key}: ${value}`); | |
| } | |
| } | |
| return result.join('\n'); | |
| } | |
| // Копирование в буфер обмена | |
| function copyToClipboard(text) { | |
| const textarea = document.createElement('textarea'); | |
| textarea.value = text; | |
| textarea.style.position = 'fixed'; | |
| textarea.style.left = '-9999px'; | |
| document.body.appendChild(textarea); | |
| textarea.select(); | |
| document.execCommand('copy'); | |
| document.body.removeChild(textarea); | |
| } | |
| // Функция показа уведомлений | |
| function showNotification(message, type = 'success') { | |
| const notification = document.createElement('div'); | |
| const colors = { | |
| success: '#4CAF50', | |
| warning: '#FF9800', | |
| error: '#F44336', | |
| info: '#2196F3' | |
| }; | |
| notification.textContent = message; | |
| notification.style.cssText = ` | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| background: ${colors[type]}; | |
| color: white; | |
| padding: 16px 24px; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.15); | |
| z-index: 10002; | |
| font-size: 14px; | |
| font-weight: 500; | |
| animation: slideIn 0.3s ease; | |
| `; | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes slideIn { | |
| from { | |
| transform: translateX(400px); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateX(0); | |
| opacity: 1; | |
| } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.style.transition = 'all 0.3s ease'; | |
| notification.style.transform = 'translateX(400px)'; | |
| notification.style.opacity = '0'; | |
| setTimeout(() => document.body.removeChild(notification), 300); | |
| }, 3000); | |
| } | |
| // Создание кнопки обычного копирования | |
| const copyButton = document.createElement('button'); | |
| copyButton.innerHTML = '📋'; | |
| copyButton.title = 'Копировать данные товара'; | |
| copyButton.style.cssText = ` | |
| position: fixed; | |
| bottom: 80px; | |
| right: 20px; | |
| z-index: 10000; | |
| background: #005bff; | |
| color: white; | |
| border: none; | |
| padding: 12px; | |
| border-radius: 50%; | |
| font-size: 20px; | |
| cursor: pointer; | |
| box-shadow: 0 2px 10px rgba(0,91,255,0.3); | |
| transition: all 0.2s; | |
| width: 48px; | |
| height: 48px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| `; | |
| document.body.appendChild(copyButton); | |
| // Обработчик обычного копирования | |
| copyButton.addEventListener('click', function() { | |
| try { | |
| const data = extractData(); | |
| const text = formatData(data); | |
| copyToClipboard(text); | |
| // Фидбэк | |
| copyButton.innerHTML = '✅'; | |
| copyButton.style.background = '#28a745'; | |
| setTimeout(() => { | |
| copyButton.innerHTML = '📋'; | |
| copyButton.style.background = '#005bff'; | |
| }, 1500); | |
| } catch (error) { | |
| copyButton.innerHTML = '❌'; | |
| copyButton.style.background = '#dc3545'; | |
| setTimeout(() => { | |
| copyButton.innerHTML = '📋'; | |
| copyButton.style.background = '#005bff'; | |
| }, 1500); | |
| console.error('Ошибка:', error); | |
| } | |
| }); | |
| // Ховер эффект | |
| copyButton.addEventListener('mouseenter', () => { | |
| copyButton.style.transform = 'scale(1.1)'; | |
| }); | |
| copyButton.addEventListener('mouseleave', () => { | |
| copyButton.style.transform = 'scale(1)'; | |
| }); | |
| // Создание кнопки накопительного копирования | |
| const accumulateButton = document.createElement('button'); | |
| accumulateButton.innerHTML = ` | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| <path d="M19 11H13V5C13 4.45 12.55 4 12 4C11.45 4 11 4.45 11 5V11H5C4.45 11 4 11.45 4 12C4 12.55 4.45 13 5 13H11V19C11 19.55 11.45 20 12 20C12.55 20 13 19.55 13 19V13H19C19.55 13 20 12.55 20 12C20 11.45 19.55 11 19 11Z" fill="white"/> | |
| </svg> | |
| `; | |
| accumulateButton.title = 'Добавить товар в буфер'; | |
| accumulateButton.style.cssText = ` | |
| position: fixed; | |
| bottom: 140px; | |
| right: 20px; | |
| z-index: 10000; | |
| background: #005bff; | |
| color: white; | |
| border: none; | |
| padding: 12px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| box-shadow: 0 2px 10px rgba(0,91,255,0.3); | |
| transition: all 0.2s; | |
| width: 48px; | |
| height: 48px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| `; | |
| // Счётчик накопленных товаров | |
| const counterBadge = document.createElement('div'); | |
| counterBadge.style.cssText = ` | |
| position: absolute; | |
| top: -6px; | |
| right: -6px; | |
| background: #FF5722; | |
| color: white; | |
| border-radius: 50%; | |
| min-width: 20px; | |
| height: 20px; | |
| font-size: 11px; | |
| font-weight: bold; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| border: 2px solid white; | |
| padding: 0 4px; | |
| `; | |
| function updateCounter() { | |
| const count = getAccumulatedCount(); | |
| counterBadge.textContent = count; | |
| counterBadge.style.display = count > 0 ? 'flex' : 'none'; | |
| } | |
| updateCounter(); | |
| accumulateButton.appendChild(counterBadge); | |
| document.body.appendChild(accumulateButton); | |
| // Обработчик накопительного копирования | |
| accumulateButton.addEventListener('click', function() { | |
| try { | |
| const data = extractData(); | |
| const text = formatData(data); | |
| const result = addToAccumulatedData(text); | |
| if (result.isDuplicate) { | |
| // Дубликат найден | |
| showNotification('Этот товар уже в буфере', 'warning'); | |
| } else { | |
| // Успешно добавлено | |
| updateCounter(); | |
| showNotification('Товар добавлен в буфер', 'success'); | |
| accumulateButton.style.background = '#28a745'; | |
| setTimeout(() => { | |
| accumulateButton.style.background = '#005bff'; | |
| }, 300); | |
| } | |
| } catch (error) { | |
| showNotification('Ошибка при добавлении', 'error'); | |
| console.error('Ошибка:', error); | |
| } | |
| }); | |
| // Ховер эффект | |
| accumulateButton.addEventListener('mouseenter', () => { | |
| accumulateButton.style.transform = 'scale(1.1)'; | |
| accumulateButton.style.boxShadow = '0 4px 15px rgba(0,91,255,0.4)'; | |
| }); | |
| accumulateButton.addEventListener('mouseleave', () => { | |
| accumulateButton.style.transform = 'scale(1)'; | |
| accumulateButton.style.boxShadow = '0 2px 10px rgba(0,91,255,0.3)'; | |
| }); | |
| // Панель настроек | |
| function createSettingsPanel() { | |
| const overlay = document.createElement('div'); | |
| overlay.style.cssText = ` | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0,0,0,0.5); | |
| z-index: 10001; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| `; | |
| const panel = document.createElement('div'); | |
| panel.style.cssText = ` | |
| background: white; | |
| padding: 28px; | |
| border-radius: 16px; | |
| box-shadow: 0 8px 32px rgba(0,0,0,0.15); | |
| min-width: 360px; | |
| max-width: 90%; | |
| `; | |
| const title = document.createElement('h3'); | |
| title.textContent = 'Настройки копирования'; | |
| title.style.cssText = ` | |
| margin: 0 0 20px 0; | |
| color: #1a1a1a; | |
| font-size: 20px; | |
| font-weight: 600; | |
| `; | |
| panel.appendChild(title); | |
| const options = [ | |
| { key: 'copyTitle', label: 'Название' }, | |
| { key: 'copyArticle', label: 'Артикул' }, | |
| { key: 'copyPrice', label: 'Цена' }, | |
| { key: 'copyOldPrice', label: 'Старая цена' }, | |
| { key: 'copyComposition', label: 'Состав' }, | |
| { key: 'copyComplectation', label: 'Комплектация' }, | |
| { key: 'copyUsage', label: 'Способ применения' }, | |
| { key: 'copyCharacteristics', label: 'Характеристики' } | |
| ]; | |
| options.forEach(opt => { | |
| const label = document.createElement('label'); | |
| label.style.cssText = ` | |
| display: flex; | |
| align-items: center; | |
| margin-bottom: 14px; | |
| cursor: pointer; | |
| user-select: none; | |
| `; | |
| const checkbox = document.createElement('input'); | |
| checkbox.type = 'checkbox'; | |
| checkbox.checked = settings[opt.key]; | |
| checkbox.style.cssText = ` | |
| margin-right: 10px; | |
| width: 18px; | |
| height: 18px; | |
| cursor: pointer; | |
| `; | |
| checkbox.addEventListener('change', () => { | |
| settings[opt.key] = checkbox.checked; | |
| saveSettings(settings); | |
| }); | |
| const labelText = document.createElement('span'); | |
| labelText.textContent = opt.label; | |
| labelText.style.cssText = 'color: #333; font-size: 15px;'; | |
| label.appendChild(checkbox); | |
| label.appendChild(labelText); | |
| panel.appendChild(label); | |
| }); | |
| // Разделитель | |
| const hr = document.createElement('hr'); | |
| hr.style.cssText = 'margin: 20px 0; border: none; border-top: 1px solid #e0e0e0;'; | |
| panel.appendChild(hr); | |
| // Заголовок секции буфера | |
| const bufferTitle = document.createElement('div'); | |
| bufferTitle.textContent = 'Накопительный буфер'; | |
| bufferTitle.style.cssText = ` | |
| font-size: 16px; | |
| font-weight: 600; | |
| color: #1a1a1a; | |
| margin-bottom: 12px; | |
| `; | |
| panel.appendChild(bufferTitle); | |
| // Информация о накопительном буфере | |
| const bufferInfo = document.createElement('div'); | |
| bufferInfo.style.cssText = ` | |
| margin-bottom: 16px; | |
| color: #666; | |
| font-size: 14px; | |
| padding: 10px; | |
| background: #f5f5f5; | |
| border-radius: 8px; | |
| `; | |
| const count = getAccumulatedCount(); | |
| bufferInfo.textContent = `Товаров в буфере: ${count}`; | |
| panel.appendChild(bufferInfo); | |
| // Контейнер для кнопок | |
| const buttonContainer = document.createElement('div'); | |
| buttonContainer.style.cssText = 'display: flex; gap: 8px; flex-direction: column;'; | |
| // Кнопка копирования из буфера | |
| const copyBufferBtn = document.createElement('button'); | |
| copyBufferBtn.textContent = 'Скопировать из буфера'; | |
| copyBufferBtn.style.cssText = ` | |
| padding: 12px 16px; | |
| background: ${count > 0 ? '#005bff' : '#ccc'}; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: ${count > 0 ? 'pointer' : 'not-allowed'}; | |
| font-size: 14px; | |
| font-weight: 500; | |
| transition: all 0.2s; | |
| `; | |
| copyBufferBtn.disabled = count === 0; | |
| if (count > 0) { | |
| copyBufferBtn.addEventListener('mouseenter', () => { | |
| copyBufferBtn.style.background = '#0047cc'; | |
| }); | |
| copyBufferBtn.addEventListener('mouseleave', () => { | |
| copyBufferBtn.style.background = '#005bff'; | |
| }); | |
| } | |
| copyBufferBtn.addEventListener('click', () => { | |
| const accumulated = getAccumulatedData(); | |
| if (accumulated) { | |
| copyToClipboard(accumulated); | |
| showNotification('Буфер скопирован в clipboard', 'success'); | |
| } | |
| }); | |
| buttonContainer.appendChild(copyBufferBtn); | |
| // Кнопка очистки буфера | |
| const clearBtn = document.createElement('button'); | |
| clearBtn.textContent = 'Очистить буфер'; | |
| clearBtn.style.cssText = ` | |
| padding: 12px 16px; | |
| background: ${count > 0 ? '#FF5722' : '#ccc'}; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: ${count > 0 ? 'pointer' : 'not-allowed'}; | |
| font-size: 14px; | |
| font-weight: 500; | |
| transition: all 0.2s; | |
| `; | |
| clearBtn.disabled = count === 0; | |
| if (count > 0) { | |
| clearBtn.addEventListener('mouseenter', () => { | |
| clearBtn.style.background = '#E64A19'; | |
| }); | |
| clearBtn.addEventListener('mouseleave', () => { | |
| clearBtn.style.background = '#FF5722'; | |
| }); | |
| } | |
| clearBtn.addEventListener('click', () => { | |
| if (confirm('Очистить накопительный буфер?')) { | |
| clearAccumulatedData(); | |
| bufferInfo.textContent = 'Товаров в буфере: 0'; | |
| updateCounter(); | |
| showNotification('Буфер очищен', 'info'); | |
| document.body.removeChild(overlay); | |
| } | |
| }); | |
| buttonContainer.appendChild(clearBtn); | |
| // Кнопка закрытия | |
| const closeBtn = document.createElement('button'); | |
| closeBtn.textContent = 'Закрыть'; | |
| closeBtn.style.cssText = ` | |
| padding: 12px 16px; | |
| background: #f5f5f5; | |
| color: #333; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: 500; | |
| margin-top: 4px; | |
| transition: all 0.2s; | |
| `; | |
| closeBtn.addEventListener('mouseenter', () => { | |
| closeBtn.style.background = '#e0e0e0'; | |
| }); | |
| closeBtn.addEventListener('mouseleave', () => { | |
| closeBtn.style.background = '#f5f5f5'; | |
| }); | |
| closeBtn.addEventListener('click', () => document.body.removeChild(overlay)); | |
| buttonContainer.appendChild(closeBtn); | |
| panel.appendChild(buttonContainer); | |
| overlay.appendChild(panel); | |
| overlay.addEventListener('click', (e) => { | |
| if (e.target === overlay) { | |
| document.body.removeChild(overlay); | |
| } | |
| }); | |
| document.body.appendChild(overlay); | |
| } | |
| // Кнопка настроек | |
| const settingsButton = document.createElement('button'); | |
| settingsButton.innerHTML = '⚙️'; | |
| settingsButton.title = 'Настройки копирования'; | |
| settingsButton.style.cssText = ` | |
| position: fixed; | |
| bottom: 200px; | |
| right: 20px; | |
| z-index: 10000; | |
| background: #6c757d; | |
| color: white; | |
| border: none; | |
| padding: 10px; | |
| border-radius: 50%; | |
| font-size: 18px; | |
| cursor: pointer; | |
| box-shadow: 0 2px 8px rgba(108,117,125,0.3); | |
| width: 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: all 0.2s; | |
| `; | |
| settingsButton.addEventListener('mouseenter', () => { | |
| settingsButton.style.transform = 'scale(1.1)'; | |
| settingsButton.style.background = '#5a6268'; | |
| }); | |
| settingsButton.addEventListener('mouseleave', () => { | |
| settingsButton.style.transform = 'scale(1)'; | |
| settingsButton.style.background = '#6c757d'; | |
| }); | |
| settingsButton.addEventListener('click', createSettingsPanel); | |
| document.body.appendChild(settingsButton); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment