您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
采集阿里巴巴国际站商品工作台产品的信息
// ==UserScript== // @name 阿里巴巴商品工作台数据导出工具--树洞先生 // @namespace http://tampermonkey.net/ // @version 1.0 // @license MIT // @author Assistant // @description 采集阿里巴巴国际站商品工作台产品的信息 // @match https://hz-productposting.alibaba.com/product_operate/product_growth.htm* // @grant none // ==/UserScript== (function() { 'use strict'; // 全局变量 let allProducts = []; // 存储所有收集到的商品数据 let isCollecting = false; // 标记是否正在收集数据 let currentPage = 1; // 当前页码 let totalPages = 1; // 总页数 // 创建主界面 function createMainInterface() { // 创建主按钮,点击后弹出主对话框 const toolButton = document.createElement('button'); toolButton.textContent = '📊 商品数据导出--树洞'; toolButton.style.cssText = ` position: fixed; top: 120px; right: 20px; z-index: 9999; padding: 12px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: bold; box-shadow: 0 4px 15px rgba(0,0,0,0.2); transition: all 0.3s ease; `; // 鼠标悬停效果 toolButton.onmouseover = () => { toolButton.style.transform = 'translateY(-2px)'; toolButton.style.boxShadow = '0 6px 20px rgba(0,0,0,0.3)'; }; toolButton.onmouseout = () => { toolButton.style.transform = 'translateY(0)'; toolButton.style.boxShadow = '0 4px 15px rgba(0,0,0,0.2)'; }; toolButton.onclick = showMainDialog; // 点击按钮显示主对话框 document.body.appendChild(toolButton); } // 显示主对话框 function showMainDialog() { // 创建对话框容器 const dialog = document.createElement('div'); dialog.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10000; background: white; border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); padding: 30px; width: 500px; max-height: 80vh; overflow-y: auto; `; // 设置对话框内容,包括状态、按钮、调试信息等 dialog.innerHTML = ` <div style="text-align: center; margin-bottom: 25px;"> <h2 style="margin: 0; color: #333; font-size: 24px;">📊 商品工作台数据导出工具--树洞</h2> <p style="margin: 10px 0 0 0; color: #666;">根据自己商品工作台页面筛选结果进行采集,可以快速采集重铺品</p> </div> <div style="margin-bottom: 20px;"> <div style="display: flex; justify-content: space-between; margin-bottom: 10px;"> <span style="color: #333; font-weight: bold;">收集状态:</span> <span id="collection-status" style="color: #666;">待开始</span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 10px;"> <span style="color: #333; font-weight: bold;">当前页面:</span> <span id="current-page-info" style="color: #666;">-</span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 10px;"> <span style="color: #333; font-weight: bold;">已收集商品:</span> <span id="collected-count" style="color: #666;">0</span> </div> <div style="background: #f5f5f5; border-radius: 8px; padding: 4px; margin-bottom: 15px;"> <div id="progress-bar" style="background: linear-gradient(90deg, #4CAF50, #45a049); height: 20px; border-radius: 6px; width: 0%; transition: width 0.3s ease;"></div> </div> </div> <div style="display: flex; gap: 10px; margin-bottom: 20px;"> <button id="start-collection" style="flex: 1; padding: 12px; background: #4CAF50; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">🔄 开始收集数据</button> <button id="stop-collection" style="flex: 1; padding: 12px; background: #f44336; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;" disabled>⏹️ 停止收集</button> </div> <div style="display: flex; gap: 10px; margin-bottom: 20px;"> <button id="export-all" style="flex: 1; padding: 12px; background: #2196F3; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;" disabled>📥 导出所有数据</button> <button id="clear-data" style="flex: 1; padding: 12px; background: #FF9800; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">🗑️ 清空数据</button> </div> <div style="display: flex; gap: 10px;"> <button id="test-extraction" style="flex: 1; padding: 12px; background: #9C27B0; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">🧪 测试提取</button> <button id="close-dialog" style="flex: 1; padding: 12px; background: #666; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">❌ 关闭</button> </div> <div id="debug-info" style="margin-top: 20px; padding: 15px; background: #f9f9f9; border-radius: 6px; font-family: monospace; font-size: 12px; max-height: 200px; overflow-y: auto; display: none;"> <strong>调试信息:</strong><br> <div id="debug-content"></div> </div> `; // 创建遮罩层,点击遮罩层可关闭对话框 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: 9999; `; overlay.appendChild(dialog); document.body.appendChild(overlay); // 绑定对话框按钮事件 bindDialogEvents(overlay); } // 绑定对话框事件,包括按钮点击等 function bindDialogEvents(overlay) { const startBtn = overlay.querySelector('#start-collection'); // 开始收集按钮 const stopBtn = overlay.querySelector('#stop-collection'); // 停止收集按钮 const exportBtn = overlay.querySelector('#export-all'); // 导出数据按钮 const clearBtn = overlay.querySelector('#clear-data'); // 清空数据按钮 const testBtn = overlay.querySelector('#test-extraction'); // 测试提取按钮 const closeBtn = overlay.querySelector('#close-dialog'); // 关闭对话框按钮 startBtn.onclick = startCollection; // 绑定开始收集事件 stopBtn.onclick = stopCollection; // 绑定停止收集事件 exportBtn.onclick = exportAllData; // 绑定导出数据事件 clearBtn.onclick = clearAllData; // 绑定清空数据事件 testBtn.onclick = testExtraction; // 绑定测试提取事件 closeBtn.onclick = () => overlay.remove(); // 关闭对话框 // 点击遮罩层关闭对话框 overlay.onclick = (e) => { if (e.target === overlay) overlay.remove(); }; } // 数字格式处理函数 - 修复数字分割问题 function parseNumber(text) { if (!text) return ''; // 移除所有逗号和空格,保留数字、小数点和百分号 const cleaned = text.replace(/[,-\s]/g, ''); // 如果包含百分号,保留百分号 if (cleaned.includes('%')) { return cleaned; } // 如果是纯数字,返回清理后的数字 const number = parseFloat(cleaned); if (!isNaN(number)) { return number.toString(); } // 如果无法解析,返回原始文本 return text; } // 基于实际DOM结构提取商品数据 - 修复数字格式问题 function extractProductsFromCurrentPage() { const products = []; try { // 基于实际DOM结构,使用正确的选择器,遍历每一行商品 const productRows = document.querySelectorAll('div[role="row"].next-row'); console.log(`找到 ${productRows.length} 个商品行`); productRows.forEach((row, index) => { try { const product = {}; // 商品图片 const imgElement = row.querySelector('._informationImg_gr0vo_49 img'); product.imageUrl = imgElement ? imgElement.src : ''; // 商品标题 const titleElement = row.querySelector('._informationText_gr0vo_67'); product.title = titleElement ? titleElement.textContent.trim() : ''; // 商品ID const idElement = row.querySelector('._informationId_gr0vo_79'); product.id = idElement ? idElement.textContent.replace('ID:', '').trim() : ''; // 商品标签 const tagElements = row.querySelectorAll('._everyTag_gr0vo_92'); product.tags = Array.from(tagElements).map(tag => tag.textContent.trim()).join(', '); // 访问数据 - 修复数字格式问题 const visitElements = row.querySelectorAll('._visitPut_gr0vo_126'); visitElements.forEach(element => { const fullText = element.textContent.trim(); const spanElement = element.querySelector('span'); const rawValue = spanElement ? spanElement.textContent.trim() : ''; const value = parseNumber(rawValue); // 使用数字格式处理函数 // 根据不同的文本内容,提取不同的访问数据 if (fullText.includes('近30天搜索曝光数')) { product.searchExposure = value; } else if (fullText.includes('近30天访问人数')) { product.visitors = value; } else if (fullText.includes('近90天[TM+询盘]人数')) { product.inquiries = value; } else if (fullText.includes('近90天[TM+询盘]转化')) { product.inquiryConversion = value; } else if (fullText.includes('近90天支付买家数')) { product.payingBuyers = value; } else if (fullText.includes('近90天访客到支付转化率')) { product.paymentConversion = value; } }); // 优化建议 const suggestionElements = row.querySelectorAll('._oneOptimization_gr0vo_170'); if (suggestionElements.length > 0) { // 采集第一个优化建议块的所有文字内容(包含子元素) const parentDiv = suggestionElements[0].closest('div'); if (parentDiv) { let suggestionText = parentDiv.innerText.trim(); // 过滤无关提示词 suggestionText = suggestionText.replace(/去完成|配置服务/g, ''); product.suggestions = suggestionText.trim(); } else { let suggestionText = Array.from(suggestionElements).map(el => el.textContent.trim()).join('; '); suggestionText = suggestionText.replace(/去完成|配置服务/g, ''); product.suggestions = suggestionText.trim(); } } else { product.suggestions = ''; } // 只添加有ID的商品 if (product.id) { product.index = allProducts.length + products.length + 1; product.pageNumber = currentPage; products.push(product); } } catch (error) { // 单个商品提取出错时,打印错误但不中断整体流程 console.error(`处理第 ${index + 1} 个商品时出错:`, error); } }); } catch (error) { // 整体提取出错时,打印错误 console.error('提取商品数据时出错:', error); } return products; } // 测试数据提取,显示调试信息 function testExtraction() { const debugInfo = document.querySelector('#debug-info'); const debugContent = document.querySelector('#debug-content'); debugInfo.style.display = 'block'; debugContent.innerHTML = '正在测试数据提取...<br>'; try { const products = extractProductsFromCurrentPage(); debugContent.innerHTML += `✅ 成功提取到 ${products.length} 个商品<br>`; if (products.length > 0) { debugContent.innerHTML += '<br><strong>第一个商品示例:</strong><br>'; const firstProduct = products[0]; Object.keys(firstProduct).forEach(key => { debugContent.innerHTML += `${key}: ${firstProduct[key]}<br>`; }); // 测试数字格式处理 debugContent.innerHTML += '<br><strong>数字格式测试:</strong><br>'; debugContent.innerHTML += `原始: "3,897" -> 处理后: "${parseNumber('3,897')}"<br>`; debugContent.innerHTML += `原始: "2.5%" -> 处理后: "${parseNumber('2.5%')}"<br>`; debugContent.innerHTML += `原始: "1,234,567" -> 处理后: "${parseNumber('1,234,567')}"<br>`; } else { debugContent.innerHTML += '<br>❌ 未找到商品数据<br>'; } } catch (error) { debugContent.innerHTML += `❌ 测试失败: ${error.message}<br>`; console.error('测试提取时出错:', error); } } // 开始收集数据 - 修复最后一页卡住问题 async function startCollection() { if (isCollecting) return; // 如果已经在收集则不重复执行 isCollecting = true; updateUI(); try { // 获取总页数 await getTotalPages(); // 从第一页开始收集 currentPage = 1; // 循环遍历每一页,收集数据 while (currentPage <= totalPages && isCollecting) { updateStatus(`正在收集第 ${currentPage} 页数据...`); // 提取当前页面数据 const pageProducts = extractProductsFromCurrentPage(); allProducts.push(...pageProducts); updateUI(); // 检查是否为最后一页 - 修复卡住问题 if (currentPage >= totalPages) { updateStatus(`收集完成!共收集 ${allProducts.length} 个商品`); break; } // 如果不是最后一页,翻到下一页 if (isCollecting) { const success = await goToNextPage(); if (!success) { updateStatus('翻页失败,收集停止'); break; } currentPage++; // 等待页面加载 await sleep(3000); // 增加等待时间确保页面完全加载 } } if (isCollecting) { updateStatus(`收集完成!共收集 ${allProducts.length} 个商品`); } } catch (error) { updateStatus(`收集过程中出错: ${error.message}`); console.error('收集数据时出错:', error); } finally { isCollecting = false; updateUI(); } } // 停止收集 function stopCollection() { isCollecting = false; // 设置标志位,主循环会自动停止 updateStatus('收集已停止'); updateUI(); } // 获取总页数 - 改进页数检测 async function getTotalPages() { try { // 查找分页信息,使用更精确的方法 const paginationElements = document.querySelectorAll('.next-pagination-item'); let maxPage = 1; paginationElements.forEach(element => { const ariaLabel = element.getAttribute('aria-label'); if (ariaLabel && ariaLabel.includes('共') && ariaLabel.includes('页')) { // 从aria-label中提取总页数,如"第1页,共76页" const match = ariaLabel.match(/共(\d+)页/); if (match) { const pageNum = parseInt(match[1]); if (pageNum > maxPage) { maxPage = pageNum; } } } // 备用方法:从按钮文本中获取页码 const text = element.textContent.trim(); const pageNum = parseInt(text); if (!isNaN(pageNum) && pageNum > maxPage) { maxPage = pageNum; } }); totalPages = maxPage; console.log(`检测到总页数: ${totalPages}`); } catch (error) { console.error('获取总页数时出错:', error); totalPages = 1; } } // 翻到下一页 - 改进翻页逻辑 async function goToNextPage() { try { // 查找下一页按钮 const nextButton = document.querySelector('.next-pagination-item.next-next'); if (nextButton && !nextButton.disabled && !nextButton.classList.contains('next-disabled')) { console.log(`点击下一页按钮,从第 ${currentPage} 页到第 ${currentPage + 1} 页`); nextButton.click(); // 等待页面开始加载 await sleep(1000); // 检查页面是否真的翻页了 let attempts = 0; const maxAttempts = 10; while (attempts < maxAttempts) { await sleep(500); // 检查当前页码是否已更新 const currentPageElement = document.querySelector('.next-pagination-item.next-current'); if (currentPageElement) { const displayedPage = parseInt(currentPageElement.textContent.trim()); if (displayedPage === currentPage + 1) { console.log(`页面已成功翻到第 ${displayedPage} 页`); return true; } } attempts++; } console.log('翻页超时,可能页面加载缓慢'); return true; // 即使超时也继续,避免卡住 } else { console.log('下一页按钮不可用或已到最后一页'); return false; } } catch (error) { console.error('翻页时出错:', error); return false; } } // 导出所有数据 function exportAllData() { if (allProducts.length === 0) { alert('没有数据可导出'); return; } try { // 生成CSV内容并下载 const csvContent = generateCSV(allProducts); downloadCSV(csvContent, `阿里巴巴商品数据_${new Date().toISOString().split('T')[0]}.csv`); updateStatus(`已导出 ${allProducts.length} 个商品数据`); } catch (error) { alert(`导出失败: ${error.message}`); console.error('导出数据时出错:', error); } } // 生成CSV内容 function generateCSV(products) { // 定义表头 const headers = [ '序号', '商品ID', '商品标题', '商品图片链接','商品标签', '近30天搜索曝光数', '近30天访问人数', '近90天询盘人数', '近90天询盘转化率', '近90天支付买家数', '近90天支付转化率', '优化建议' ]; // 添加BOM头,防止中文乱码 let csvContent = '\uFEFF' + headers.join(',') + '\n'; // 遍历每个商品,生成每一行 products.forEach(product => { const row = [ product.index || '', product.id || '', `"${(product.title || '').replace(/"/g, '""')}"`, `"${(product.tags || '').replace(/"/g, '""')}"`, product.imageUrl || '', product.searchExposure || '', product.visitors || '', product.inquiries || '', product.inquiryConversion || '', product.payingBuyers || '', product.paymentConversion || '', `"${(product.suggestions || '').replace(/"/g, '""')}"` ]; csvContent += row.join(',') + '\n'; }); return csvContent; } // 下载CSV文件 function downloadCSV(content, filename) { // 创建Blob对象并生成下载链接 const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); if (link.download !== undefined) { const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', filename); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } } // 清空所有数据 function clearAllData() { if (confirm('确定要清空所有收集的数据吗?')) { allProducts = []; currentPage = 1; updateStatus('数据已清空'); updateUI(); } } // 更新状态显示 function updateStatus(message) { const statusElement = document.querySelector('#collection-status'); if (statusElement) { statusElement.textContent = message; } console.log(message); } // 更新UI,包括状态、进度条、按钮状态等 function updateUI() { const statusElement = document.querySelector('#collection-status'); const pageInfoElement = document.querySelector('#current-page-info'); const countElement = document.querySelector('#collected-count'); const progressBar = document.querySelector('#progress-bar'); const startBtn = document.querySelector('#start-collection'); const stopBtn = document.querySelector('#stop-collection'); const exportBtn = document.querySelector('#export-all'); if (pageInfoElement) { pageInfoElement.textContent = `${currentPage} / ${totalPages}`; } if (countElement) { countElement.textContent = allProducts.length; } if (progressBar && totalPages > 0) { const progress = (currentPage / totalPages) * 100; progressBar.style.width = `${Math.min(progress, 100)}%`; } if (startBtn) { startBtn.disabled = isCollecting; } if (stopBtn) { stopBtn.disabled = !isCollecting; } if (exportBtn) { exportBtn.disabled = allProducts.length === 0; } } // 工具函数:延时,返回Promise function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 初始化,等待页面加载完成后创建主界面 function init() { // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', createMainInterface); } else { createMainInterface(); } } // 启动脚本 init(); })();