// ==UserScript==
// @name Ozon Product Info Export to Excel with Draggable UI
// @namespace http://tampermonkey.net/
// @version 4.4
// @description 获取Ozon商品信息并导出到Excel,带可拖动的用户界面和版权信息,优化关键词获取
// @author Nicole
// @match https://www.ozon.ru/*
// @grant GM_xmlhttpRequest
// @grant GM_download
// @connect *
// @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.1/xlsx.full.min.js
// @icon https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif
// @supportURL https://gitcode.com/nicole2088/ozon
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 样式定义
const styles = `
#ozon-export-container {
position: fixed;
top: 10px;
right: 10px;
background-color: #ffffff;
border: 2px solid #007bff;
border-radius: 10px;
padding: 15px;
z-index: 10000;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
font-family: Arial, sans-serif;
width: 320px;
cursor: move;
}
#ozon-export-container h2 {
margin-top: 0;
color: #007bff;
font-size: 16px;
cursor: move;
}
#ozon-export-container .instruction {
font-size: 14px;
color: #333;
margin-top: 5px;
}
#ozon-export-container button {
padding: 8px 15px;
margin-top: 10px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
#ozon-export-container button:hover {
background-color: #0056b3;
}
#ozon-export-container .progress-container {
margin-top: 15px;
}
#ozon-export-container .progress-container progress {
width: 100%;
height: 15px;
}
#ozon-export-container .close-btn {
position: absolute;
top: 5px;
right: 10px;
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: #007bff;
}
#ozon-export-container .status-text {
font-size: 14px;
margin-bottom: 5px;
}
#ozon-export-container .footer {
margin-top: 10px;
text-align: right;
font-size: 12px;
color: #888;
}
`;
// 添加样式到页面
function addStyles(css) {
const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
// 创建用户界面
function createUI() {
const container = document.createElement('div');
container.id = 'ozon-export-container';
container.innerHTML = `
<button class="close-btn">×</button>
<h2>Ozon商品信息抓取小助手v4.4</h2>
<p class="instruction">请在商品页面加载完成后进行导出</p>
<div style="display: flex; align-items: center; justify-content: space-between;">
<button id="export-btn">导出到Excel</button>
<span class="footer">Made by Nicole</span>
</div>
<div class="progress-container" style="display:none;">
<p id="progressStatus" class="status-text">准备中...</p>
<progress id="progressBar" value="0" max="100"></progress>
</div>
`;
document.body.appendChild(container);
container.querySelector('.close-btn').addEventListener('click', () => {
container.style.display = 'none';
});
container.querySelector('#export-btn').addEventListener('click', collectAndExport);
makeDraggable(container);
return {
progressContainer: container.querySelector('.progress-container'),
progressBar: container.querySelector('#progressBar'),
progressStatus: container.querySelector('#progressStatus'),
container: container
};
}
// 添加拖动功能
function makeDraggable(element) {
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
const header = element.querySelector('h2');
header.style.cursor = 'move';
header.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - element.offsetLeft;
offsetY = e.clientY - element.offsetTop;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
});
function mouseMoveHandler(e) {
if (isDragging) {
element.style.left = (e.clientX - offsetX) + 'px';
element.style.top = (e.clientY - offsetY) + 'px';
element.style.right = 'auto';
}
}
function mouseUpHandler() {
isDragging = false;
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
}
}
// 获取商品名称
function getProductName() {
const nameElement = document.querySelector('h1.m5t_27.tsHeadline550Medium');
return nameElement ? nameElement.innerText.trim() : '无商品名称';
}
// 获取商品图片URL
function getProductImages() {
const images = [];
const imageElements = document.querySelectorAll('div.k1q_27.qk1_27 img');
imageElements.forEach((img) => {
if (img.src) {
images.push(img.src);
}
});
return images;
}
// 获取商品详情(存储条件和组成)
function getProductDetails() {
const details = [];
const detailSections = document.querySelectorAll('.RA-a1 h3');
detailSections.forEach((section) => {
const title = section.innerText.trim();
const content = section.nextElementSibling ? section.nextElementSibling.innerText.trim() : '';
details.push(`${title}: ${content}`);
});
return details.join('\n');
}
// 获取商品信息
function getProductInfo() {
const info = [];
const infoElements = document.querySelectorAll('.y8m_27 .my9_27');
infoElements.forEach((item) => {
const label = item.querySelector('.tsBodyM');
const value = item.querySelector('.tsBody400Small');
if (label && value) {
info.push(`${label.innerText.trim()}: ${value.innerText.trim()}`);
}
});
return info.join(', ');
}
// 获取关键词
function getKeywords() {
const keywordElements = document.querySelectorAll('.re5_10 .e4r_10');
let keywords = [];
keywordElements.forEach((el) => {
keywords.push(el.innerText.trim());
});
return keywords.join(', ');
}
// 获取视频URL
function getVideoUrl() {
const videoElement = document.querySelector('video[qa-id="video-player.video-element"]');
return videoElement ? videoElement.src : null;
}
// 导出为Excel
function exportToExcel(data) {
const ws = XLSX.utils.aoa_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Product Info");
const range = XLSX.utils.decode_range(ws['!ref']);
// 设置表头样式
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 0, c: C });
if (!ws[cellAddress]) continue;
ws[cellAddress].s = {
font: { bold: true, color: { rgb: "FFFFFF" } },
fill: { fgColor: { rgb: "007BFF" } },
alignment: { horizontal: "center", vertical: "center" },
border: {
top: { style: "thin", color: { rgb: "000000" } },
bottom: { style: "thin", color: { rgb: "000000" } },
left: { style: "thin", color: { rgb: "000000" } },
right: { style: "thin", color: { rgb: "000000" } },
}
};
}
// 设置内容样式
for (let R = range.s.r + 1; R <= range.e.r; ++R) {
const fillColor = R % 2 === 0 ? "DDEBF7" : "FFFFFF";
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: R, c: C });
if (!ws[cellAddress]) continue;
ws[cellAddress].s = {
alignment: { horizontal: "center", vertical: "center" },
fill: { fgColor: { rgb: fillColor } },
border: {
top: { style: "thin", color: { rgb: "CCCCCC" } },
bottom: { style: "thin", color: { rgb: "CCCCCC" } },
left: { style: "thin", color: { rgb: "CCCCCC" } },
right: { style: "thin", color: { rgb: "CCCCCC" } },
}
};
}
}
ws['!cols'] = [
{ wpx: 200 }, // 商品名称
{ wpx: 300 }, // 商品图片
{ wpx: 250 }, // 商品信息
{ wpx: 180 }, // 关键词
{ wpx: 200 } // 商品详情
];
XLSX.writeFile(wb, "ozon_product_info_styled.xlsx");
}
// 下载商品图片
function downloadImages(images, progressBar, progressStatus) {
const total = images.length;
let completed = 0;
images.forEach((imageUrl, index) => {
GM_download({
url: imageUrl,
name: `product_image_${index + 1}.jpg`,
saveAs: false,
onload: () => {
completed++;
const progress = ((completed) / total) * 100;
progressBar.value = progress;
progressStatus.innerText = `下载图片 ${completed} / ${total}`;
},
onerror: (error) => {
console.error('下载图片失败:', error);
progressStatus.innerText = `下载图片 ${index + 1} 失败`;
}
});
});
}
// 下载视频
function downloadVideo(videoUrl, progressBar, progressStatus) {
if (videoUrl) {
GM_download({
url: videoUrl,
name: `product_video.mp4`,
saveAs: false,
onload: () => {
progressBar.value = 100;
progressStatus.innerText = `视频下载完成`;
},
onerror: (error) => {
console.error('下载视频失败:', error);
progressStatus.innerText = `视频下载失败`;
}
});
}
}
// 创建用户界面并返回控制元素
const ui = createUI();
// 更新进度
function updateProgress(progressBar, progressStatus, progress, message) {
progressBar.value = progress;
progressStatus.innerText = message;
}
// 主功能:抓取信息并导出到Excel
function collectAndExport() {
ui.progressContainer.style.display = 'block';
updateProgress(ui.progressBar, ui.progressStatus, 0, '开始抓取商品信息...');
setTimeout(() => {
const productName = getProductName();
updateProgress(ui.progressBar, ui.progressStatus, 10, '获取商品名称');
const productImages = getProductImages();
updateProgress(ui.progressBar, ui.progressStatus, 30, '获取商品图片');
const productDetails = getProductDetails();
updateProgress(ui.progressBar, ui.progressStatus, 50, '获取商品详情');
const productInfo = getProductInfo();
updateProgress(ui.progressBar, ui.progressStatus, 70, '获取商品信息');
const keywords = getKeywords();
updateProgress(ui.progressBar, ui.progressStatus, 80, '获取关键词');
const videoUrl = getVideoUrl();
updateProgress(ui.progressBar, ui.progressStatus, 85, '检查视频');
const productData = [
["商品名称", "商品图片", "商品信息", "关键词", "商品详情"],
[productName, productImages.join(','), productInfo, keywords, productDetails]
];
exportToExcel(productData);
updateProgress(ui.progressBar, ui.progressStatus, 90, '数据已导出到Excel');
if (productImages.length > 0) {
updateProgress(ui.progressBar, ui.progressStatus, 90, '开始下载图片...');
downloadImages(productImages, ui.progressBar, ui.progressStatus);
}
if (videoUrl) {
updateProgress(ui.progressBar, ui.progressStatus, 95, '开始下载视频...');
downloadVideo(videoUrl, ui.progressBar, ui.progressStatus);
}
updateProgress(ui.progressBar, ui.progressStatus, 100, '操作完成');
}, 100);
}
// 添加样式
addStyles(styles);
// 在用户打开 https://www.ozon.ru/ 时显示界面
if (window.location.href === 'https://www.ozon.ru/' || window.location.href === 'https://www.ozon.ru') {
ui.container.style.display = 'block';
}
})();