Skip to content

Instantly share code, notes, and snippets.

@qFamouse
Last active December 4, 2025 08:30
Show Gist options
  • Select an option

  • Save qFamouse/baf5ee80a4630744c089f725c3c03a30 to your computer and use it in GitHub Desktop.

Select an option

Save qFamouse/baf5ee80a4630744c089f725c3c03a30 to your computer and use it in GitHub Desktop.
Копирование данных с карточки товара Ozon
// ==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