您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
采集阿里巴巴产品360分析关键词数据
// ==UserScript== // @name 阿里巴巴产品360分析数据采集 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 采集阿里巴巴产品360分析关键词数据 // @author 树洞先生 // @license MIT // @match https://data.alibaba.com/product/overview* // @grant GM_xmlhttpRequest // @grant GM_download // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/xlsx.full.min.js // ==/UserScript== (function() { 'use strict'; // 从cookie中提取ctoken function extractCtokenFromCookie() { const match = document.cookie.match(/ctoken=([^;&]+)/); return match ? match[1] : 'axky2vnha4zf'; } // 生成动态callback function generateCallback() { const timestamp = Date.now(); const randomNum = Math.floor(Math.random() * 90000) + 10000; return `jsonp_${timestamp}_${randomNum}`; } // 计算当前月份和周数(使用ISO标准) function getCurrentTimeInfo() { const now = new Date(); const currentMonth = now.getMonth() + 1; // getMonth()返回0-11 const currentYear = now.getFullYear(); // 使用ISO标准计算当前是第几周 const startOfYear = new Date(currentYear, 0, 1); const days = Math.floor((now - startOfYear) / (24 * 60 * 60 * 1000)); const weekNum = Math.ceil((days + startOfYear.getDay() + 1) / 7); return { currentMonth, currentYear, currentWeek: weekNum }; } // 计算目标时间段 function calculateTimePeriod(statisticsType, selected) { const { currentMonth, currentYear, currentWeek } = getCurrentTimeInfo(); const now = new Date(); if (statisticsType === "month") { // 月份计算逻辑 let targetMonth; if (now.getDate() <= 5) { // 北京时间低于5号,时间段为当前月份减2 targetMonth = currentMonth - 2; } else { // 超过5号,时间段为当前月份减1 targetMonth = currentMonth - 1; } let targetYear = currentYear; if (targetMonth <= 0) { targetMonth += 12; targetYear = currentYear - 1; } return `${targetYear}-${targetMonth.toString().padStart(2, '0')}`; } else { // 周数计算逻辑(ISO标准) let targetWeek; if (now.getDay() === 1) { // 周一 // 使用ISO标准是周一,时间段为当前周次减3 targetWeek = currentWeek - 3; } else { // 使用ISO标准不是周一,时间段为当前周次减2 targetWeek = currentWeek - 2; } let targetYear = currentYear; if (targetWeek <= 0) { targetWeek += 52; // 假设一年52周 targetYear = currentYear - 1; } return `${targetYear}-W${targetWeek.toString().padStart(2, '0')}`; } } // 创建用户界面 function createUI() { // 等待产品详情区域加载 waitForElement('.area-title', function(productDetailArea) { if (productDetailArea) { console.log('找到产品详情区域,创建按钮'); createButtonInArea(productDetailArea); } else { console.log('未找到产品详情区域,使用默认位置'); createDefaultUI(); } }); } // 等待元素出现的函数 function waitForElement(selector, callback, maxAttempts = 30) { let attempts = 0; function checkElement() { const element = document.querySelector(selector); if (element) { callback(element); } else if (attempts < maxAttempts) { attempts++; setTimeout(checkElement, 1000); // 每秒检查一次 } else { console.log(`等待元素 ${selector} 超时,使用默认位置`); callback(null); } } checkElement(); } // 在产品详情区域创建按钮 function createButtonInArea(productDetailArea) { // 找到 area-title 下所有 span const spans = productDetailArea.querySelectorAll('span'); // 取第三个(下标2),如果没有则兜底用最后一个 let targetSpan = spans[2] || spans[spans.length - 1] || productDetailArea; // 创建按钮 const triggerBtn = document.createElement('button'); triggerBtn.textContent = '产品360分析'; triggerBtn.style.cssText = ` display: inline-block; vertical-align: middle; margin-left: 10px; background: #007bff; color: white; border: none; border-radius: 4px; padding: 4px 12px; cursor: pointer; font-family: Arial, sans-serif; font-size: 14px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); `; targetSpan.insertAdjacentElement('afterend', triggerBtn); const panel = createPanel(); document.body.appendChild(panel); bindPanelEvents(triggerBtn); } // 创建弹窗HTML内容 function createPanelHTML() { return ` <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"> <h3 style="margin: 0; color: #007bff;">产品360分析数据采集</h3> <button id="closePanel" style="background: none; border: none; font-size: 18px; cursor: pointer; color: #666;">×</button> </div> <div style="margin-bottom: 10px;"> <label>产品ID(可批量):</label> <textarea id="prodId" rows="4" placeholder="每行一个产品ID" style="width: 100%; margin-top: 5px; resize: vertical;"></textarea> </div> <div style="margin-bottom: 10px;"> <label>终端类型:</label> <select id="terminalType" style="width: 100%; margin-top: 5px;"> <option value="TOTAL">TOTAL</option> <option value="PC">PC</option> <option value="APP">APP</option> <option value="WAP">WAP</option> </select> </div> <div style="margin-bottom: 10px;"> <label>统计类型:</label> <select id="statisticsType" style="width: 100%; margin-top: 5px;"> <option value="week">week</option> <option value="month">month</option> </select> </div> <div style="margin-bottom: 10px;"> <label>时间段选择:</label> <input type="number" id="selected" value="1" min="1" style="width: 100%; margin-top: 5px;"> <small style="color: #666;">1=最近一周/月,2=上两周/月,依此类推</small> </div> <div style="margin-bottom: 15px;"> <button id="startCollect" style="background: #007bff; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; width: 100%;"> 开始采集 </button> </div> <div id="status" style="font-size: 12px; color: #666;"></div> `; } // 创建弹窗容器 function createPanel() { const panel = document.createElement('div'); panel.id = 'product360Panel'; panel.style.cssText = ` position: fixed; top: 50px; right: 10px; background: white; border: 2px solid #007bff; border-radius: 8px; padding: 15px; z-index: 10000; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-family: Arial, sans-serif; min-width: 300px; display: none; `; panel.innerHTML = createPanelHTML(); return panel; } // 绑定弹窗事件 function bindPanelEvents(triggerBtn) { triggerBtn.addEventListener('click', function() { const panel = document.getElementById('product360Panel'); if (panel.style.display === 'none' || panel.style.display === '') { panel.style.display = 'block'; } else { panel.style.display = 'none'; } }); document.getElementById('closePanel').addEventListener('click', function() { document.getElementById('product360Panel').style.display = 'none'; }); document.getElementById('startCollect').addEventListener('click', startCollection); } // 创建默认UI(当找不到产品详情区域时使用) function createDefaultUI() { // 创建触发按钮 const triggerBtn = document.createElement('button'); triggerBtn.textContent = '产品360分析'; triggerBtn.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #007bff; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; z-index: 10001; font-family: Arial, sans-serif; font-size: 14px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); `; const panel = createPanel(); document.body.appendChild(triggerBtn); document.body.appendChild(panel); bindPanelEvents(triggerBtn); } // 并发池实现 function createLimit(max) { let running = 0, queue = []; function next() { if (queue.length && running < max) { running++; queue.shift()().finally(() => { running--; next(); }); } } return fn => new Promise((resolve, reject) => { queue.push(() => fn().then(resolve, reject)); next(); }); } // 开始采集 async function startCollection() { const prodIdRaw = document.getElementById('prodId').value; const terminalType = document.getElementById('terminalType').value; const statisticsType = document.getElementById('statisticsType').value; const selected = document.getElementById('selected').value; const statusDiv = document.getElementById('status'); // 处理批量ID let prodIds = prodIdRaw.split(/\r?\n/).map(x => x.trim()).filter(x => x); prodIds = Array.from(new Set(prodIds)); // 去重 if (prodIds.length === 0) { statusDiv.innerHTML = '请至少输入一个产品ID'; return; } statusDiv.innerHTML = '正在采集数据...'; try { const ctoken = extractCtokenFromCookie(); const callback = generateCallback(); const timePeriod = calculateTimePeriod(statisticsType, selected); const { currentMonth, currentYear, currentWeek } = getCurrentTimeInfo(); let wb = XLSX.utils.book_new(); let totalCount = 0; let finished = 0; const limit = createLimit(5); // 最多5个并发 async function collectOneProduct(prodId, idx) { let page = 1; let allRecords = []; while (true) { const params = { action: 'OneAction', iName: 'vip/product/360/wordAnalysis/content', isVip: 'true', terminalType: terminalType, statisticType: 'os', selected: selected, statisticsType: statisticsType, prodId: prodId, pageCount: page.toString(), ctoken: ctoken, callback: callback, _: Date.now().toString() }; const response = await fetchData(params); if (!response.success) { statusDiv.innerHTML = `产品ID:${prodId} 第${page}页请求失败: ${response.error}`; break; } if (response.data.length === 0) { break; } // 处理数据 const processedRecords = response.data.map(record => { if (record.p4pState) { Object.keys(record.p4pState).forEach(key => { record[`p4pState_${key}`] = record.p4pState[key]; }); delete record.p4pState; } // 添加用户输入参数 record.产品ID = prodId; record.终端类型 = terminalType; record.统计类型 = statisticsType; record.时间段 = timePeriod; return record; }); allRecords.push(...processedRecords); page++; await new Promise(resolve => setTimeout(resolve, 500)); } // 字段映射 const fieldMap = { searchKeyword: '关键词', searchImps: '搜索曝光次数', searchClicks: '搜索点击次数', p4pExposureCnt: '直通车曝光次数', p4pClickCnt: '直通车点击次数', detailUv: '商品详情页访问人数', fbUv: '店内询盘人数', tmUv: '店内TM咨询人数', crtOrdUv: '店内订单买家人数', uvAbRate: '商机转化率', p4pState_isAllowAdded: '是否允许添加到直通车', p4pState_isP4pKeyword: '是否为直通车关键词', 产品ID: '产品ID', 终端类型: '终端类型', 统计类型: '统计类型', 时间段: '时间段' }; // 处理数据 const processedData = allRecords.map(record => { const newRecord = {}; Object.keys(fieldMap).forEach(key => { if (record[key] !== undefined) { newRecord[fieldMap[key]] = record[key]; } }); // 商机转化率转为百分号格式 if (newRecord['商机转化率'] !== undefined) { newRecord['商机转化率'] = (newRecord['商机转化率'] * 100).toFixed(2) + '%'; } return newRecord; }); totalCount += processedData.length; const ws = XLSX.utils.json_to_sheet(processedData); XLSX.utils.book_append_sheet(wb, ws, prodId); finished++; statusDiv.innerHTML = `已完成${finished}/${prodIds.length}个产品ID采集...`; } await Promise.all( prodIds.map((prodId, idx) => limit(() => collectOneProduct(prodId, idx))) ); // 文件名逻辑 let filename = ''; if (prodIds.length === 1) { filename = `${prodIds[0]}_360分析关键词信息.xlsx`; } else { filename = `多个产品ID_360分析关键词信息.xlsx`; } XLSX.writeFile(wb, filename); statusDiv.innerHTML = `采集完成!共${totalCount}条数据,已保存到 ${filename}`; } catch (error) { statusDiv.innerHTML = `采集失败: ${error.message}`; console.error('采集错误:', error); } } // 获取数据 function fetchData(params) { return new Promise((resolve) => { const queryString = Object.keys(params) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&'); GM_xmlhttpRequest({ method: 'GET', url: `https://hz-mydata.alibaba.com/self/.json?${queryString}`, headers: { 'Accept': '*/*', 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6', 'Referer': 'https://data.alibaba.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36' }, onload: function(response) { try { const match = response.responseText.match(/jsonp_\d+_\d+\((.*)\)/); if (match) { const data = JSON.parse(match[1]); resolve({ success: true, data: data.data || [] }); } else { resolve({ success: false, error: '无法解析响应数据' }); } } catch (error) { resolve({ success: false, error: `解析错误: ${error.message}` }); } }, onerror: function(error) { resolve({ success: false, error: `请求失败: ${error.message}` }); } }); }); } // 初始化 createUI(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址