淘宝商品销量抓取工具

从淘宝搜索页面按照销量排序抓取商品销量数据并导出Excel

// ==UserScript==
// @name         淘宝商品销量抓取工具
// @namespace    http://tampermonkey.net/
// @license MIT
// @version      0.2
// @description  从淘宝搜索页面按照销量排序抓取商品销量数据并导出Excel
// @author       GitHub Copilot
// @match        https://*.s.taobao.com/search*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        unsafeWindow
// @connect      taobao.com
// @connect      tmall.com
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// ==/UserScript==

(function() {
    'use strict';
    // 禁用淘宝的一些屏蔽措施
    function disableAntiCrawl() {
        try {
            // 尝试禁用一些可能的拦截器
            if (unsafeWindow.XMLHttpRequest.prototype._originalOpen === undefined) {
                unsafeWindow.XMLHttpRequest.prototype._originalOpen = unsafeWindow.XMLHttpRequest.prototype.open;
                unsafeWindow.XMLHttpRequest.prototype.open = function() {
                    // 避免请求被中断
                    this.addEventListener('error', function(e) {
                        console.log('XHR 错误被捕获', e);
                    });
                    this.addEventListener('abort', function(e) {
                        console.log('XHR 中断被捕获', e);
                    });
                    return this._originalOpen.apply(this, arguments);
                };
            }
            
            // // 防止页面跳转打断脚本执行
            // window.onbeforeunload = function(e) {
            //     // 如果正在爬取数据,阻止页面跳转
            //     if (GM_getValue('isScrapingActive', false)) {
            //         e = e || window.event;
            //         // 对于现代浏览器
            //         e.returnValue = '正在抓取数据,确定要离开吗?';
            //         // 对于旧浏览器
            //         return '正在抓取数据,确定要离开吗?';
            //     }
            // };
            
            // 禁用可能的反爬虫JavaScript
            const scriptTags = document.querySelectorAll('script');
            scriptTags.forEach(script => {
                if (script.innerHTML.includes('crawler') || 
                    script.innerHTML.includes('spider') || 
                    script.innerHTML.includes('bot')) {
                    script.innerHTML = '';
                }
            });
        } catch (e) {
            console.error('禁用反爬虫措施失败:', e);
        }
    }

    // 创建浮动工具面板
    function createToolPanel() {
        // 删除可能已存在的面板
        const existingPanel = document.getElementById('sales-crawler-panel');
        if (existingPanel) {
            existingPanel.remove();
        }
        
        const panel = document.createElement('div');
        panel.id = 'sales-crawler-panel';
        panel.style.cssText = `
            position: fixed;
            top: 100px;
            right: 20px;
            width: 320px;
            background: #fff;
            border: none;
            border-radius: 8px;
            box-shadow: 0 3px 15px rgba(0,0,0,0.15);
            z-index: 9999;
            padding: 0;
            font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
            transition: all 0.3s ease;
            overflow: hidden;
        `;

        // 面板内容
        panel.innerHTML = `
            <div class="panel-header" style="
                padding: 12px 15px;
                background: linear-gradient(135deg, #ff6a00, #ff8533);
                color: white;
                font-weight: bold;
                border-radius: 8px 8px 0 0;
                display: flex;
                justify-content: space-between;
                align-items: center;
                user-select: none;
            ">
                <div style="display: flex; align-items: center;">
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;">
                        <polyline points="23 4 23 10 17 10"></polyline>
                        <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
                    </svg>
                    <span>淘宝商品销量抓取</span>
                </div>
                <div style="display: flex; align-items: center;">
                    <button id="minimize-btn" style="
                        background: none;
                        border: none;
                        color: white;
                        cursor: pointer;
                        font-size: 18px;
                        padding: 0 8px;
                    ">−</button>
                </div>
            </div>
            
            <div id="panel-content" style="padding: 15px;">
                <div class="input-group" style="margin-bottom: 12px;">
                    <label style="display: block; font-size: 13px; color: #666; margin-bottom: 5px;">搜索关键词</label>
                    <input id="keyword-input" type="text" placeholder="请输入要搜索的商品关键词" style="
                        width: 100%;
                        padding: 10px;
                        box-sizing: border-box;
                        border: 1px solid #e0e0e0;
                        border-radius: 6px;
                        font-size: 14px;
                        transition: border 0.2s;
                    " />
                </div>
                
                <div style="display: flex; margin-bottom: 12px; gap: 10px;">
                    <div class="input-group" style="flex: 1;">
                        <label style="display: block; font-size: 13px; color: #666; margin-bottom: 5px;">最大页数</label>
                        <input id="max-pages" type="number" value="3" min="1" max="100" style="
                            width: 100%;
                            padding: 10px;
                            box-sizing: border-box;
                            border: 1px solid #e0e0e0;
                            border-radius: 6px;
                            font-size: 14px;
                        " />
                    </div>
                    
                    <div class="input-group" style="flex: 1;">
                        <label style="display: block; font-size: 13px; color: #666; margin-bottom: 5px;">排序方式</label>
                        <select id="sort-type" style="
                            width: 100%;
                            padding: 10px;
                            box-sizing: border-box;
                            border: 1px solid #e0e0e0;
                            border-radius: 6px;
                            font-size: 14px;
                            background-color: white;
                        ">
                            <option value="sale-desc" selected>销量降序</option>
                            <option value="price-asc">价格升序</option>
                            <option value="price-desc">价格降序</option>
                            <option value="renqi-desc">人气降序</option>
                        </select>
                    </div>
                </div>
                
                <div class="btn-group" style="display: flex; justify-content: space-between; margin-bottom: 15px; gap: 10px;">
                    <button id="search-btn" style="
                        flex: 1;
                        padding: 10px 15px;
                        background: #ff6a00;
                        color: white;
                        border: none;
                        border-radius: 6px;
                        cursor: pointer;
                        font-size: 14px;
                        font-weight: bold;
                        transition: background 0.2s;
                    ">开始抓取</button>
                    <button id="download-btn" style="
                        flex: 1;
                        padding: 10px 15px;
                        background: #2ecc71;
                        color: white;
                        border: none;
                        border-radius: 6px;
                        cursor: pointer;
                        font-size: 14px;
                        font-weight: bold;
                        transition: background 0.2s;
                    " disabled>下载Excel</button>
                </div>
                
                <div id="status" style="
                    font-size: 13px;
                    color: #666;
                    text-align: center;
                    margin: 10px 0;
                    padding: 8px;
                    border-radius: 4px;
                    background-color: #f9f9f9;
                    min-height: 18px;
                ">准备就绪</div>
                
                <div class="progress-container" style="
                    margin-bottom: 15px;
                    background-color: #f0f0f0;
                    border-radius: 4px;
                    height: 8px;
                    overflow: hidden;
                    display: none;
                ">
                    <div id="progress-bar" style="
                        height: 100%;
                        width: 0%;
                        background-color: #4CAF50;
                        transition: width 0.3s;
                    "></div>
                </div>
                
                <div id="results" style="
                    margin-top: 10px;
                    max-height: 350px;
                    overflow-y: auto;
                    border-top: 1px solid #eee;
                    padding-top: 10px;
                "></div>
            </div>
            
            <div id="panel-footer" style="
                padding: 10px 15px;
                border-top: 1px solid #eee;
                background-color: #f9f9f9;
                font-size: 12px;
                color: #888;
                text-align: center;
            ">
                版本 0.2 · <a href="#" id="help-btn" style="color: #ff6a00; text-decoration: none;">使用帮助</a>
            </div>
        `;

        document.body.appendChild(panel);
        
        // 添加事件监听器
        setupPanelEventListeners(panel);
        
        // 添加拖拽功能
        makeDraggable(panel);
        
        return panel;
    }

    // 设置面板事件监听器
    function setupPanelEventListeners(panel) {
        // 最小化/最大化按钮功能
        const minimizeBtn = panel.querySelector('#minimize-btn');
        const panelContent = panel.querySelector('#panel-content');
        const panelFooter = panel.querySelector('#panel-footer');
        
        minimizeBtn.addEventListener('click', () => {
            if (panelContent.style.display === 'none') {
                // 展开面板
                panelContent.style.display = 'block';
                panelFooter.style.display = 'block';
                minimizeBtn.textContent = '−';
            } else {
                // 折叠面板
                panelContent.style.display = 'none';
                panelFooter.style.display = 'none';
                minimizeBtn.textContent = '+';
            }
        });
        
        // 输入框焦点效果
        const inputs = panel.querySelectorAll('input[type="text"], input[type="number"]');
        inputs.forEach(input => {
            input.addEventListener('focus', () => {
                input.style.border = '1px solid #ff6a00';
            });
            
            input.addEventListener('blur', () => {
                input.style.border = '1px solid #e0e0e0';
            });
        });
        
        // 按钮悬停效果
        const searchBtn = panel.querySelector('#search-btn');
        searchBtn.addEventListener('mouseover', () => {
            searchBtn.style.background = '#ff8533';
        });
        
        searchBtn.addEventListener('mouseout', () => {
            searchBtn.style.background = '#ff6a00';
        });
        
        const downloadBtn = panel.querySelector('#download-btn');
        downloadBtn.addEventListener('mouseover', () => {
            if (!downloadBtn.disabled) {
                downloadBtn.style.background = '#27ae60';
            }
        });
        
        downloadBtn.addEventListener('mouseout', () => {
            downloadBtn.style.background = '#2ecc71';
        });
        
        // 帮助按钮功能
        const helpBtn = panel.querySelector('#help-btn');
        helpBtn.addEventListener('click', (e) => {
            e.preventDefault();
            showHelp();
        });
        
        // 添加回车键搜索功能
        const keywordInput = panel.querySelector('#keyword-input');
        keywordInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter' && !searchBtn.disabled) {
                searchBtn.click();
            }
        });
    }

    // 显示帮助信息
    function showHelp() {
        // 创建帮助弹窗
        const helpModal = document.createElement('div');
        helpModal.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 10000;
        `;
        
        helpModal.innerHTML = `
            <div style="
                width: 500px;
                background-color: white;
                border-radius: 8px;
                padding: 20px;
                box-shadow: 0 5px 20px rgba(0,0,0,0.2);
            ">
                <h2 style="margin-top: 0; color: #ff6a00;">淘宝商品销量抓取工具使用帮助</h2>
                <div style="margin-bottom: 20px;">
                    <h3>功能简介</h3>
                    <p>本工具可以帮助您抓取淘宝搜索结果中的商品销量数据,并支持导出为Excel表格。</p>
                    
                    <h3>使用方法</h3>
                    <ol>
                        <li>在"搜索关键词"输入框中输入您想要搜索的商品关键词</li>
                        <li>设置要抓取的最大页数(每页约44个商品)</li>
                        <li>选择排序方式(默认为销量降序)</li>
                        <li>点击"开始抓取"按钮开始抓取数据</li>
                        <li>抓取完成后,可以点击"下载Excel"按钮导出数据</li>
                    </ol>
                    
                    <h3>注意事项</h3>
                    <ul>
                        <li>抓取过程中请不要关闭或刷新页面</li>
                        <li>如需中途停止,可点击"停止抓取"按钮</li>
                        <li>抓取大量数据时可能会被淘宝识别为异常行为,建议设置合理的页数</li>
                        <li>本工具仅供学习研究使用,请勿用于商业用途</li>
                    </ul>
                </div>
                <div style="text-align: center;">
                    <button id="close-help" style="
                        padding: 8px 15px;
                        background: #ff6a00;
                        color: white;
                        border: none;
                        border-radius: 4px;
                        cursor: pointer;
                    ">关闭</button>
                </div>
            </div>
        `;
        
        document.body.appendChild(helpModal);
        
        // 点击关闭按钮或背景关闭帮助
        helpModal.querySelector('#close-help').addEventListener('click', () => {
            helpModal.remove();
        });
        
        helpModal.addEventListener('click', (e) => {
            if (e.target === helpModal) {
                helpModal.remove();
            }
        });
    }

    // 更新进度条
    function updateProgress(current, max) {
        const progressContainer = document.querySelector('.progress-container');
        const progressBar = document.getElementById('progress-bar');
        
        if (progressContainer && progressBar) {
            // 显示进度条
            progressContainer.style.display = 'block';
            
            // 计算进度百分比
            const percentage = Math.round((current / max) * 100);
            
            // 更新进度条宽度
            progressBar.style.width = `${percentage}%`;
            
            // 根据进度改变颜色
            if (percentage < 30) {
                progressBar.style.backgroundColor = '#ff6a00';
            } else if (percentage < 70) {
                progressBar.style.backgroundColor = '#ffaa33';
            } else {
                progressBar.style.backgroundColor = '#4CAF50';
            }
        }
    }

    // 添加面板拖动功能
    function makeDraggable(element) {
        // 存储初始位置和鼠标位置
        let startX, startY, startLeft, startTop;
        let isDragging = false;
        
        // 获取面板头部元素作为拖拽手柄
        const header = element.querySelector('.panel-header');
        if (!header) return;
        
        // 确保面板有正确的定位样式
        element.style.position = 'fixed';
        element.style.margin = '0';
        
        // 添加拖拽指示样式
        header.style.cursor = 'move';
        header.style.userSelect = 'none';
        
        // 鼠标按下事件
        header.addEventListener('mousedown', startDrag);
        
        function startDrag(e) {
            // 防止文本选择
            e.preventDefault();
            
            // 记录初始位置
            startX = e.clientX;
            startY = e.clientY;
            
            // 获取面板当前位置
            const rect = element.getBoundingClientRect();
            startLeft = rect.left;
            startTop = rect.top;
            
            // 标记开始拖拽
            isDragging = true;
            
            // 添加全局拖拽事件监听
            document.addEventListener('mousemove', drag);
            document.addEventListener('mouseup', stopDrag);
            
            // 添加拖拽时的视觉反馈
            element.style.transition = 'none'; // 拖拽时禁用过渡效果
            element.style.opacity = '0.9';
            element.style.boxShadow = '0 5px 20px rgba(0,0,0,0.2)';
            
            // 记录在日志中
            console.log('开始拖拽,初始位置:', {x: startX, y: startY, left: startLeft, top: startTop});
        }
        
        function drag(e) {
            if (!isDragging) return;
            
            // 计算位移
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            
            // 应用新位置
            const newLeft = startLeft + dx;
            const newTop = startTop + dy;
            
            // 确保不超出屏幕
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;
            const panelWidth = element.offsetWidth;
            const panelHeight = element.offsetHeight;
            
            // 边界检查,防止面板拖出屏幕
            const boundedLeft = Math.max(0, Math.min(newLeft, viewportWidth - panelWidth / 3));
            const boundedTop = Math.max(0, Math.min(newTop, viewportHeight - 50)); // 留出至少50px高度
            
            // 设置新位置
            element.style.left = `${boundedLeft}px`;
            element.style.top = `${boundedTop}px`;
            element.style.right = 'auto'; // 确保right样式不会干扰
            
            // 记录日志 (但不要太频繁)
            if (Math.random() < 0.05) {
                console.log('拖拽中:', {
                    dx, dy,
                    newLeft, newTop,
                    boundedLeft, boundedTop
                });
            }
        }
        
        function stopDrag() {
            if (!isDragging) return;
            
            // 标记结束拖拽
            isDragging = false;
            
            // 移除全局事件监听
            document.removeEventListener('mousemove', drag);
            document.removeEventListener('mouseup', stopDrag);
            
            // 恢复视觉样式
            element.style.transition = 'box-shadow 0.3s, opacity 0.3s';
            element.style.opacity = '1';
            element.style.boxShadow = '0 3px 15px rgba(0,0,0,0.15)';
            
            // 记录最终位置
            console.log('结束拖拽,最终位置:', {
                left: element.style.left,
                top: element.style.top
            });
        }
        
        // 允许通过触摸拖拽(移动设备支持)
        header.addEventListener('touchstart', function(e) {
            const touch = e.touches[0];
            const mouseEvent = new MouseEvent('mousedown', {
                clientX: touch.clientX,
                clientY: touch.clientY
            });
            startDrag(mouseEvent);
            e.preventDefault(); // 防止滚动
        }, { passive: false });
        
        document.addEventListener('touchmove', function(e) {
            if (!isDragging) return;
            
            const touch = e.touches[0];
            const mouseEvent = new MouseEvent('mousemove', {
                clientX: touch.clientX,
                clientY: touch.clientY
            });
            drag(mouseEvent);
            e.preventDefault(); // 防止滚动
        }, { passive: false });
        
        document.addEventListener('touchend', function() {
            stopDrag();
        });
        
        // 记录拖拽初始化完成
        console.log('拖拽功能已初始化');
    }

    // 延迟函数
    const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

    // 手动搜索并抓取
    async function manualSearch(keyword) {
        const statusElement = document.getElementById('status');
        statusElement.textContent = '准备搜索,请耐心等待...';
        
        // 构建搜索URL(按销量排序)
        // s.taobao.com/search?q=%E7%BB%BF%E8%8C%B6&sort=sale-desc&tab=all
        // https://s.taobao.com/search?page=2&q=%E7%BB%BF%E8%8C%B6&sort=sale-desc&tab=all
        const searchUrl = `https://s.taobao.com/search?q=${encodeURIComponent(keyword)}&sort=sale-desc&tab=all`;
        
        // 检查当前是否在搜索页面
        if (!window.location.href.includes('s.taobao.com/search')) {
            window.location.href = searchUrl;
            return null; // 返回null表示需要重新加载页面
        }
        
        // 检查URL参数
        const currentKeyword = getParameterByName('q', window.location.href);
        const currentSort = getParameterByName('sort', window.location.href);
        const currentPage = getParameterByName('page', window.location.href) || '1';
        
        // 如果是首页但关键词或排序不匹配,则跳转
        if (currentKeyword !== keyword || currentSort !== 'sale-desc') {
            window.location.href = searchUrl;
            return null;
        }
        
        // 更新状态显示当前页码
        statusElement.textContent = `正在加载第 ${currentPage} 页数据...`;
        
        // 等待页面元素加载完成
        await waitForPageLoad();
        
        // 提取当前页面的商品数据
        return extractItemsFromCurrentPage();
    }

// 等待页面加载完成
function waitForPageLoad() {
    return new Promise(resolve => {
        // 定义可能的淘宝商品列表选择器
        const selectors = [
            '.m-itemlist .items .item',     // 经典淘宝列表
            '.doubleCard--gO3Bz6bu',        // 新版淘宝卡片
            '.items .item',                 // 通用项目选择器
            '.J_MouserOnverReq',            // 淘宝鼠标悬停元素
            '.grid .item',                  // 网格布局商品
            'div[data-index]',              // 带索引的商品项
            '.contentHolder--gO3Bz6bu',     // 新版淘宝内容容器
            '.commonCard--gO3Bz6bu'         // 新版淘宝通用卡片
        ];
        
        // 首先检查页面是否已经加载了商品元素
        for (const selector of selectors) {
            const elements = document.querySelectorAll(selector);
            if (elements && elements.length > 0) {
                console.log(`页面已加载,找到 ${elements.length} 个商品,选择器: ${selector}`);
                return setTimeout(resolve, 800); // 短暂延迟确保完全加载
            }
        }
        
        // 设置最大尝试次数和计数器
        let attempts = 0;
        const maxAttempts = 30; // 最多等待30次,约15秒
        
        // 定时检查元素是否出现
        const checkInterval = setInterval(() => {
            attempts++;
            
            // 检查所有可能的选择器
            for (const selector of selectors) {
                const elements = document.querySelectorAll(selector);
                if (elements && elements.length > 0) {
                    clearInterval(checkInterval);
                    if (observer) observer.disconnect();
                    console.log(`第 ${attempts} 次检查: 找到 ${elements.length} 个商品,选择器: ${selector}`);
                    return setTimeout(resolve, 800); // 短暂延迟确保完全加载
                }
            }
            
            // 超时处理
            if (attempts >= maxAttempts) {
                clearInterval(checkInterval);
                if (observer) observer.disconnect();
                console.log(`等待页面加载超时(${maxAttempts * 500}ms),继续执行`);
                return resolve();
            }
            
            console.log(`等待页面加载中...(${attempts}/${maxAttempts})`);
        }, 500);
        
        // 使用MutationObserver监听DOM变化,更快地检测元素出现
        let observer = null;
        try {
            observer = new MutationObserver(mutations => {
                for (const selector of selectors) {
                    const elements = document.querySelectorAll(selector);
                    if (elements && elements.length > 0) {
                        clearInterval(checkInterval);
                        observer.disconnect();
                        console.log(`观察器检测到商品元素,选择器: ${selector}`);
                        return setTimeout(resolve, 800); // 短暂延迟确保完全加载
                    }
                }
            });
            
            // 观察整个文档的变化
            observer.observe(document.body, { 
                childList: true, 
                subtree: true,
                attributes: false, 
                characterData: false
            });
        } catch (e) {
            console.error('创建MutationObserver失败:', e);
        }
        
        // 设置最终超时保护,确保不会永远等待
        setTimeout(() => {
            clearInterval(checkInterval);
            if (observer) observer.disconnect();
            console.log('最终超时保护触发,强制继续执行');
            resolve();
        }, maxAttempts * 500 + 2000);
    });
}
    // 获取URL参数
    function getParameterByName(name, url = window.location.href) {
        name = name.replace(/[\[\]]/g, '\\$&');
        const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
        const results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }

    // 从当前页面提取商品数据
    function extractItemsFromCurrentPage() {
        const items = [];
        let productItems = [];
        
        // 尝试多种可能的选择器
        const selectors = [
            '.m-itemlist .items .item',
            '.doubleCard--gO3Bz6bu',
            '.items .item',
            '.J_MouserOnverReq',
            '[data-index]'
        ];
        
        // 找到有效的选择器
        for (const selector of selectors) {
            const elements = document.querySelectorAll(selector);
            if (elements && elements.length > 0) {
                productItems = elements;
                console.log(`使用选择器 ${selector} 找到 ${elements.length} 个商品`);
                break;
            }
        }
        
        if (productItems.length === 0) {
            console.warn('未找到商品元素,请检查页面结构');
            return items;
        }
        
        // 处理每个商品项
        productItems.forEach((item, index) => {
            try {
                // 获取商品标题
                let title = '';
                const titleSelectors = [
                    '.title a', 
                    '.title--qJ7Xg_90',
                    '.ctx-box .title a',
                    '.title',
                    'a.J_ClickStat'
                ];
                
                for (const selector of titleSelectors) {
                    const titleElement = item.querySelector(selector);
                    if (titleElement) {
                        title = titleElement.textContent.trim();
                        break;
                    }
                }
                
                // 获取商品链接
                let link = '';
                const linkSelectors = [
                    '.title a',
                    'a.J_ClickStat',
                    '.pic a',
                    'a[data-nid]'
                ];
                
                for (const selector of linkSelectors) {
                    const linkElement = item.querySelector(selector);
                    if (linkElement && linkElement.href) {
                        link = linkElement.href;
                        break;
                    }
                }
                
                // 获取销量数据
                let sales = '0';
                const salesSelectors = [
                    '.deal-cnt',
                    '.realSales--XZJiepmt',
                    '.sale-num',
                    '[data-field="itemList"] [data-field="deal"]',
                    '.sale em'
                ];
                
                for (const selector of salesSelectors) {
                    const salesElement = item.querySelector(selector);
                    if (salesElement) {
                        sales = salesElement.textContent.trim();
                        // 处理销量数据
                        sales = sales.replace(/人收货|人付款|笔|付款|\+|收货/g, '');
                        break;
                    }
                }
                
                // 获取店铺名称
                let shop = '';
                const shopSelectors = [
                    '.shop .J_ShopInfo',
                    '.shopNameText--DmtlsDKm',
                    '.shop a',
                    '.shopname'
                ];
                
                for (const selector of shopSelectors) {
                    const shopElement = item.querySelector(selector);
                    if (shopElement) {
                        shop = shopElement.textContent.trim();
                        break;
                    }
                }
                
                // 获取价格
                let price = '';
                const priceSelectors = [
                    '.price strong',
                    '.priceInt--yqqZMJ5a',
                    '.price',
                    '.price em'
                ];
                
                for (const selector of priceSelectors) {
                    const priceElement = item.querySelector(selector);
                    if (priceElement) {
                        price = priceElement.textContent.trim().replace(/[^\d.]/g, '');
                        break;
                    }
                }
                
                // 添加商品数据
                items.push({
                    序号: index + 1,
                    商品名称: title,
                    店铺: shop,
                    价格: price,
                    销量: sales,
                    链接: link
                });
            } catch (error) {
                console.error('提取商品数据出错:', error);
            }
        });
        
        return items;
    }

    // 执行爬取
    async function performScraping(keyword, maxPages) {
        // 设置正在抓取的标志
        GM_setValue('isScrapingActive', true);
        
        // 更新状态
        const statusElement = document.getElementById('status');
        const resultsElement = document.getElementById('results');
        
        statusElement.textContent = '开始搜索...';
        
        try {
            // 从缓存恢复已抓取数据
            let allItems = [];
            const cachedData = GM_getValue('currentItems');
            if (cachedData) {
                try {
                    allItems = JSON.parse(cachedData);
                    console.log(`恢复了${allItems.length}条缓存数据`);
                    
                    // 显示恢复的数据
                    if (allItems.length > 0) {
                        displayResults(allItems, resultsElement);
                    }
                } catch (e) {
                    console.error('解析缓存数据出错:', e);
                    // 如果解析出错,重置数据
                    allItems = [];
                    GM_setValue('currentItems', JSON.stringify([]));
                }
            }
            
            // 检查我们当前在哪一页
            let currentPage = getPageFromUrl(window.location.href);
            console.log(`当前页码: ${currentPage}, 最大页数: ${maxPages}`);
            
            // 确保我们有正确的数据
            if (currentPage > 1 && allItems.length === 0) {
                // 如果我们在非第一页但没有之前的数据,回到第一页
                console.log('在非第一页但没有历史数据,跳回第一页');
                await navigateToPage(1, keyword);
                return;
            }
            
            // 执行搜索,可能会导致页面内容更新
            const pageItems = await manualSearch(keyword);
            if (pageItems === null) {
                // 页面将更新,中断当前执行
                console.log('搜索操作会导致页面刷新,暂停执行');
                return;
            }
            
            console.log(`从当前页面提取了 ${pageItems.length} 条商品数据`);
            
            // 将当前页数据添加到总数据集
            if (currentPage === 1) {
                // 第一页直接替换数据
                allItems = pageItems;
                console.log(`第1页:设置 ${pageItems.length} 条数据作为起始数据`);
            } else {
                // 对于后续页面,附加新数据
                console.log(`第${currentPage}页:合并数据,当前总数据量: ${allItems.length},新数据量: ${pageItems.length}`);
                
                // 改进的重复检测逻辑:同时考虑商品名称和店铺名称
                const uniqueItems = [];
                const existingItemKeys = new Set();
                
                // 先为现有数据创建唯一键集合
                allItems.forEach(item => {
                    // 创建唯一键:商品名称 + 店铺名称,这样可以更精确地识别重复商品
                    const uniqueKey = `${item.商品名称}|${item.店铺}`;
                    existingItemKeys.add(uniqueKey);
                });
                
                // 筛选新数据中不重复的项目
                pageItems.forEach(item => {
                    const uniqueKey = `${item.商品名称}|${item.店铺}`;
                    
                    // 如果该商品不存在于现有数据中,添加它
                    if (!existingItemKeys.has(uniqueKey)) {
                        // 设置正确的序号
                        item.序号 = allItems.length + uniqueItems.length + 1;
                        uniqueItems.push(item);
                        // 添加到已存在键集合中,避免当前页内的重复
                        existingItemKeys.add(uniqueKey);
                    }
                });
                
                console.log(`过滤后添加 ${uniqueItems.length} 条新数据,丢弃 ${pageItems.length - uniqueItems.length} 条重复数据`);
                
                // 合并数据
                allItems = allItems.concat(uniqueItems);
            }
            
            // 按销量排序数据 - 修改为按照销量排序
            allItems.sort((a, b) => {
                // 销量数据预处理:移除逗号、点和其他非数字字符,然后转为整数
                const salesA = parseInt(a.销量.replace(/[,\.万+]/g, '')) || 0;
                const salesB = parseInt(b.销量.replace(/[,\.万+]/g, '')) || 0;
                return salesB - salesA;  // 降序排序
            });
            
            // 重新标记序号
            allItems.forEach((item, index) => {
                item.序号 = index + 1;
            });
            
            // 保存当前数据
            GM_setValue('currentItems', JSON.stringify(allItems));
            console.log(`已保存 ${allItems.length} 条数据到缓存`);
            
            // 显示抓取结果
            statusElement.textContent = `已抓取 ${allItems.length} 条数据,当前第 ${currentPage}/${maxPages} 页`;
            
            // 显示数据
            displayResults(allItems, resultsElement);
            
            // 决定是否继续抓取下一页
            if (currentPage < maxPages) {
                statusElement.textContent = `已抓取第 ${currentPage}/${maxPages} 页,总计 ${allItems.length} 条数据,准备下一页...`;
                
                // 保存当前数据
                GM_setValue('currentItems', JSON.stringify(allItems));
                GM_setValue('currentPage', currentPage + 1);
                GM_setValue('maxPages', maxPages);
                GM_setValue('keyword', keyword);
                
                // 添加停止按钮 - 新增功能
                addStopButton();
                
                // 延迟一下以便用户查看
                await delay(1000);
                
                // 使用新函数导航到下一页,优先使用页面内按钮点击
                await navigateToPage(currentPage + 1, keyword);
            } else {
                // 全部抓取完成
                finishScraping(allItems);
            }
        } catch (error) {
            console.error('抓取过程中出错:', error);
            statusElement.textContent = '抓取过程中出错,请重试: ' + error.message;
            GM_setValue('isScrapingActive', false);
        }
    }
    // 显示结果
    function displayResults(items, resultsElement) {
        // 限制显示数量,避免浏览器卡顿
        const maxDisplayItems = 100;
        const displayItems = items.slice(0, maxDisplayItems);
        
        let html = `
            <div style="margin-top:10px;font-size:12px;">
                <p>已抓取 ${items.length} 条数据${items.length > maxDisplayItems ? `(显示前 ${maxDisplayItems} 条)` : ''}</p>
                <table style="width:100%;border-collapse:collapse;font-size:12px;">
                    <thead>
                        <tr>
                            <th style="padding:5px;border:1px solid #ddd;background:#f5f5f5;">序号</th>
                            <th style="padding:5px;border:1px solid #ddd;background:#f5f5f5;">商品名称</th>
                            <th style="padding:5px;border:1px solid #ddd;background:#f5f5f5;">价格</th>
                            <th style="padding:5px;border:1px solid #ddd;background:#f5f5f5;">销量</th>
                            <th style="padding:5px;border:1px solid #ddd;background:#f5f5f5;">店铺</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${displayItems.map(item => `
                            <tr>
                                <td style="padding:5px;border:1px solid #ddd;text-align:center;">${item.序号}</td>
                                <td style="padding:5px;border:1px solid #ddd;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${item.商品名称}">${item.商品名称 || '未知'}</td>
                                <td style="padding:5px;border:1px solid #ddd;text-align:center;">${item.价格 || '0'}</td>
                                <td style="padding:5px;border:1px solid #ddd;text-align:center;">${item.销量 || '0'}</td>
                                <td style="padding:5px;border:1px solid #ddd;">${item.店铺 || '未知'}</td>
                            </tr>
                        `).join('')}
                    </tbody>
                </table>
            </div>
        `;
        
        resultsElement.innerHTML = html;
    }

    // 继续抓取(从页面刷新后恢复)
    async function continueScraping() {
        if (!GM_getValue('isScrapingActive')) return;
        
        const keyword = GM_getValue('keyword');
        const maxPages = GM_getValue('maxPages', 3);
        
        // 当前页面URL中的页码参数
        const pageParam = getParameterByName('page') || '1';
        const currentPage = parseInt(pageParam);
        
        // 更新UI状态
        if (document.getElementById('keyword-input')) {
            document.getElementById('keyword-input').value = keyword;
        }
        
        if (document.getElementById('max-pages')) {
            document.getElementById('max-pages').value = maxPages;
        }
        
        // 禁用搜索按钮
        if (document.getElementById('search-btn')) {
            document.getElementById('search-btn').disabled = true;
        }
        
        // 继续抓取流程
        await performScraping(keyword, maxPages);
    }

    // 完成抓取
    function finishScraping(allItems) {
        const statusElement = document.getElementById('status');
        statusElement.textContent = `抓取完成,共获取 ${allItems.length} 条数据`;
        
        // 保存最终数据
        GM_setValue('finalScrapedData', JSON.stringify(allItems));
        
        // 清理中间状态
        GM_deleteValue('currentItems');
        GM_deleteValue('currentPage');
        GM_deleteValue('isScrapingActive');
        
        // 启用下载按钮
        document.getElementById('download-btn').disabled = false;
    }

    // 导出Excel
    function exportToExcel() {
        try {
            // 获取最终数据
            const data = JSON.parse(GM_getValue('finalScrapedData') || '[]');
            
            if (!data || data.length === 0) {
                alert('没有可导出的数据');
                return;
            }
            
            // 准备表头和数据
            const header = ["序号", "商品名称", "店铺", "价格", "销量", "链接"];
            const rows = data.map(item => [
                item.序号,
                item.商品名称 || '',
                item.店铺 || '',
                item.价格 || '',
                item.销量 || '0',
                item.链接 || ''
            ]);
            
            // 创建工作表
            const worksheet = XLSX.utils.aoa_to_sheet([header, ...rows]);
            
            // 设置列宽
            const wscols = [
                {wch: 6},   // 序号
                {wch: 40},  // 商品名称
                {wch: 20},  // 店铺
                {wch: 10},  // 价格
                {wch: 10},  // 销量
                {wch: 60}   // 链接
            ];
            worksheet['!cols'] = wscols;
            
            // 创建工作簿并添加工作表
            const workbook = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(workbook, worksheet, "销量数据");
            
            // 获取文件名
            const keyword = GM_getValue('keyword') || '淘宝商品';
            const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
            const fileName = `${keyword}_销量数据_${timestamp}.xlsx`;
            
            // 导出文件
            XLSX.writeFile(workbook, fileName);
            
            console.log('Excel导出成功:', fileName);
        } catch (error) {
            console.error('导出Excel出错:', error);
            alert('导出Excel失败: ' + error.message);
        }
    }

// 添加URL变更监听
function setupUrlChangeListener() {
    // 存储当前URL以便检测变化
    let lastUrl = window.location.href;
    
    // 创建一个观察器来监视URL变化
    const observer = new MutationObserver(() => {
        if (lastUrl !== window.location.href) {
            console.log(`URL已变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            
            // 处理URL变更
            handleUrlChange(oldUrl, lastUrl);
        }
    });
    
    // 开始观察document变化
    observer.observe(document, { subtree: true, childList: true });
    
    // 自定义事件监听
    window.addEventListener('urlchanged', function() {
        if (lastUrl !== window.location.href) {
            console.log(`通过自定义事件检测到URL变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            handleUrlChange(oldUrl, lastUrl);
        }
    });
    
    // 通过监听popstate事件捕获浏览器历史导航
    window.addEventListener('popstate', function() {
        if (lastUrl !== window.location.href) {
            console.log(`通过popstate检测到URL变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            handleUrlChange(oldUrl, lastUrl);
        }
    });
    
    // 通过监听hashchange事件捕获hash变更
    window.addEventListener('hashchange', function() {
        if (lastUrl !== window.location.href) {
            console.log(`通过hashchange检测到URL变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            handleUrlChange(oldUrl, lastUrl);
        }
    });
    
    // 重写history方法以捕获pushState和replaceState
    const originalPushState = history.pushState;
    history.pushState = function() {
        originalPushState.apply(this, arguments);
        if (lastUrl !== window.location.href) {
            console.log(`通过pushState检测到URL变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            handleUrlChange(oldUrl, lastUrl);
        }
    };
    
    const originalReplaceState = history.replaceState;
    history.replaceState = function() {
        originalReplaceState.apply(this, arguments);
        if (lastUrl !== window.location.href) {
            console.log(`通过replaceState检测到URL变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            handleUrlChange(oldUrl, lastUrl);
        }
    };
    
    // 定期检查URL变化(针对某些特殊情况)
    setInterval(() => {
        if (lastUrl !== window.location.href) {
            console.log(`通过轮询检测到URL变更: ${lastUrl} -> ${window.location.href}`);
            const oldUrl = lastUrl;
            lastUrl = window.location.href;
            handleUrlChange(oldUrl, lastUrl);
        }
    }, 1000);
}
    // 处理URL变更
    async function handleUrlChange(oldUrl, newUrl) {
        try {
            // 检查页面是否改变
            const oldPage = getPageFromUrl(oldUrl);
            const newPage = getPageFromUrl(newUrl);
            
            console.log(`URL变更: ${oldUrl} -> ${newUrl}`);
            console.log(`页码变更: ${oldPage} -> ${newPage}`);
            
            // 检查是否是淘宝搜索页面
            const isSearchPage = newUrl.includes('s.taobao.com/search');
            
            // 如果不是搜索页面,则不处理
            if (!isSearchPage) {
                console.log('不是搜索页面,不处理URL变更');
                return;
            }
            
            // 检查是否正在抓取
            const isActive = GM_getValue('isScrapingActive', false);
            if (!isActive) {
                console.log('没有正在进行的抓取任务,不处理URL变更');
                return;
            }
            
            // 分析URL变更是否是分页变更
            if (oldPage !== newPage) {
                console.log(`检测到页码从 ${oldPage} 变更为 ${newPage}`);
                
                // 更新当前页码
                GM_setValue('currentPage', newPage);
                
                // 等待新页面加载
                await waitForPageLoad();
                
                // 继续抓取
                const keyword = GM_getValue('keyword');
                const maxPages = GM_getValue('maxPages', 3);
                await performScraping(keyword, maxPages);
            } else {
                console.log('页码未变更,不处理');
            }
        } catch (error) {
            console.error('处理URL变更时出错:', error);
        }
    }
    // 从URL中提取页码
    function getPageFromUrl(url) {
        // 尝试获取page参数
        const pageParam = getParameterByName('page', url);
        if (pageParam) {
            return parseInt(pageParam);
        }
        
        // 尝试获取s参数(s=44对应第2页,s=88对应第3页,以此类推)
        const sParam = getParameterByName('s', url);
        if (sParam) {
            return Math.floor(parseInt(sParam) / 44) + 1;
        }
        
        // 默认为第1页
        return 1;
    }
// 导航到特定页码
async function navigateToPage(pageNum, keyword) {
    console.log(`正在导航到第 ${pageNum} 页...`);
    
    // 更新状态显示
    const statusElement = document.getElementById('status');
    if (statusElement) {
        statusElement.textContent = `正在跳转到第 ${pageNum} 页...`;
    }
    
    // 尝试点击分页按钮
    const result = await handlePagination(pageNum, keyword);
    
    // 如果点击按钮失败,尝试回退到其他方式
    if (!result) {
        console.log('点击分页按钮失败,尝试备用方案...');
        
        // 检查是否已经在正确的页面
        const currentPage = getPageFromUrl(window.location.href);
        if (currentPage === pageNum) {
            console.log(`已经在第 ${pageNum} 页,无需导航`);
            return true;
        }
        
        // 尝试点击"下一页"按钮(如果需要前进一页)
        if (pageNum === currentPage + 1) {
            const nextBtn = document.querySelector('.J_Ajax.next, .ui-page-s-next, .ui-page-next, a.next-page');
            if (nextBtn) {
                console.log('使用"下一页"按钮导航...');
                nextBtn.scrollIntoView({ behavior: 'smooth', block: 'center' });
                await delay(500);
                nextBtn.click();
                return true;
            }
        }
        
        // 最后的备选方案:直接修改URL(避免此方式但保留作为最后手段)
        console.log('无法通过点击按钮导航,尝试直接修改URL(备选方案)');
        const baseUrl = `https://s.taobao.com/search`;
        // 注意:此处修复了URL构建的问题,添加了缺失的&符号
        let targetUrl = `${baseUrl}?page=${pageNum}&q=${encodeURIComponent(keyword)}&sort=sale-desc&tab=all`;        // const targetUrl = `${baseUrl}?q=${encodeURIComponent(keyword)}&sort=sale-desc&page=${pageNum}`;
        
        // 使用pushState而不是直接修改location
        if (window.history && window.history.pushState) {
            window.history.pushState({}, '', targetUrl);
            // 触发一个自定义事件,便于我们的URL监听器捕获
            window.dispatchEvent(new Event('urlchanged'));
            return true;
        } else {
            // 最后手段
            window.location.href = targetUrl;
            return true;
        }
    }
    
    return true;
}

// 处理分页点击
async function handlePagination(pageNum, keyword) {
    try {
        console.log(`尝试点击第 ${pageNum} 页按钮...`);
        
        // 新增针对淘宝新版分页的选择器
        const nextPaginationSelectors = [
            // 新版淘宝分页按钮
            '.next-pagination-item',
            '.next-btn.next-medium.next-btn-normal.next-pagination-item',
            '.next-pagination-list button',
            // 老版淘宝分页按钮
            '.J_Ajax.num',
            '.ui-page-s-next', 
            'li.item', 
            'a[data-value]',
            '.pagination a',
            '.page-item', 
            '.page-number',
            '.pagination-item',
            '.item.J_Ajax'
        ];
        
        // 先尝试精确匹配目标页码的按钮
        let targetBtn = null;
        let pageClicked = false;

        // 尝试方法1: 使用aria-label属性查找(新版淘宝最准确的方式)
        const ariaLabelButtons = Array.from(document.querySelectorAll('button[aria-label*="页"]'));
        for (const btn of ariaLabelButtons) {
            const ariaLabel = btn.getAttribute('aria-label') || '';
            if (ariaLabel.includes(`第${pageNum}页`)) {
                console.log(`通过aria-label找到第${pageNum}页按钮: ${ariaLabel}`);
                targetBtn = btn;
                break;
            }
        }
        
        // 尝试方法2: 使用按钮内容文本匹配
        if (!targetBtn) {
            // 遍历所有可能的分页按钮
            for (const selector of nextPaginationSelectors) {
                const buttons = document.querySelectorAll(selector);
                for (const btn of buttons) {
                    // 检查按钮文本是否与目标页码匹配
                    const btnText = btn.textContent.trim();
                    if (btnText === String(pageNum)) {
                        console.log(`通过文本内容找到第${pageNum}页按钮: ${btnText}`);
                        targetBtn = btn;
                        break;
                    }
                    
                    // 检查按钮中的span元素
                    const helperSpan = btn.querySelector('.next-btn-helper');
                    if (helperSpan && helperSpan.textContent.trim() === String(pageNum)) {
                        console.log(`通过.next-btn-helper找到第${pageNum}页按钮: ${helperSpan.textContent}`);
                        targetBtn = btn;
                        break;
                    }
                }
                if (targetBtn) break;
            }
        }
        
        // 如果找到了目标按钮,执行点击
        if (targetBtn) {
            console.log(`找到第${pageNum}页按钮,准备点击...`);
            
            // 确保按钮可见
            targetBtn.scrollIntoView({ behavior: 'smooth', block: 'center' });
            await delay(500);
            
            // 尝试多种点击方式
            try {
                // 1. 常规点击
                targetBtn.click();
                console.log(`点击第${pageNum}页按钮成功`);
                pageClicked = true;
            } catch (e) {
                console.log(`常规点击失败,尝试模拟鼠标事件: ${e.message}`);
                
                // 2. 使用MouseEvent模拟点击
                try {
                    const clickEvent = new MouseEvent('click', {
                        view: window,
                        bubbles: true,
                        cancelable: true
                    });
                    targetBtn.dispatchEvent(clickEvent);
                    console.log(`使用MouseEvent点击第${pageNum}页按钮成功`);
                    pageClicked = true;
                } catch (e2) {
                    console.error(`点击事件模拟失败: ${e2.message}`);
                    
                    // 3. 触发按钮的onmousedown和onmouseup事件
                    try {
                        const mouseDown = new MouseEvent('mousedown', {
                            view: window,
                            bubbles: true,
                            cancelable: true
                        });
                        const mouseUp = new MouseEvent('mouseup', {
                            view: window,
                            bubbles: true,
                            cancelable: true
                        });
                        
                        targetBtn.dispatchEvent(mouseDown);
                        await delay(10);
                        targetBtn.dispatchEvent(mouseUp);
                        console.log(`使用mousedown/up事件点击第${pageNum}页按钮`);
                        pageClicked = true;
                    } catch (e3) {
                        console.error(`所有点击方法均失败: ${e3.message}`);
                    }
                }
            }
        }
        
        // 方法3: 如果目标页码按钮无法找到或点击失败,尝试使用"下一页"按钮
        if (!pageClicked && pageNum > 1) {
            // 先查找下一页按钮
            let nextPageBtn = null;
            
            // 针对新版淘宝下一页按钮
            const nextBtnCandidates = [
                document.querySelector('.next-btn.next-medium.next-btn-normal.next-pagination-item.next-next'),
                document.querySelector('button[aria-label*="下一页"]'),
                document.querySelector('.next-pagination-item.next-next'),
                document.querySelector('.J_Ajax.next'),
                document.querySelector('.ui-page-s-next'),
                document.querySelector('button.next-btn:has(span.next-btn-helper:contains("下一页"))'),
                document.querySelector('a.next-page')
            ];
            
            for (const btn of nextBtnCandidates) {
                if (btn) {
                    nextPageBtn = btn;
                    break;
                }
            }
            
            // 如果找到下一页按钮,尝试点击
            if (nextPageBtn) {
                console.log('使用"下一页"按钮导航...');
                nextPageBtn.scrollIntoView({ behavior: 'smooth', block: 'center' });
                await delay(500);
                
                try {
                    nextPageBtn.click();
                    console.log('"下一页"按钮点击成功');
                    pageClicked = true;
                } catch (e) {
                    console.error(`点击"下一页"按钮失败: ${e.message}`);
                    try {
                        const clickEvent = new MouseEvent('click', {
                            view: window,
                            bubbles: true,
                            cancelable: true
                        });
                        nextPageBtn.dispatchEvent(clickEvent);
                        console.log(`使用MouseEvent点击"下一页"按钮成功`);
                        pageClicked = true;
                    } catch (e2) {
                        console.error(`所有点击"下一页"方式均失败: ${e2.message}`);
                    }
                }
            }
        }
        
        // 方法4: 尝试使用跳转输入框(淘宝新版分页特有)
        if (!pageClicked) {
            const jumpInput = document.querySelector('.next-pagination-jump-input input');
            const jumpBtn = document.querySelector('.next-pagination-jump-go');
            
            if (jumpInput && jumpBtn) {
                console.log(`尝试使用跳转输入框跳转到第${pageNum}页...`);
                
                // 设置输入框值
                jumpInput.value = String(pageNum);
                
                // 触发输入框change事件
                jumpInput.dispatchEvent(new Event('change', { bubbles: true }));
                jumpInput.dispatchEvent(new Event('input', { bubbles: true }));
                
                await delay(300);
                
                // 点击确定按钮
                try {
                    jumpBtn.click();
                    console.log('跳转按钮点击成功');
                    pageClicked = true;
                } catch (e) {
                    console.log(`点击跳转按钮失败: ${e.message}`);
                    try {
                        const clickEvent = new MouseEvent('click', {
                            view: window,
                            bubbles: true,
                            cancelable: true
                        });
                        jumpBtn.dispatchEvent(clickEvent);
                        console.log(`使用MouseEvent点击跳转按钮成功`);
                        pageClicked = true;
                    } catch (e2) {
                        console.error(`所有点击跳转按钮方式均失败: ${e2.message}`);
                    }
                }
            }
        }
        
        // 如果所有点击方法都失败,记录详细的分页元素信息
        if (!pageClicked) {
            console.log('所有点击方法都失败,记录分页元素信息:');
            
            // 打印所有可能的分页元素的HTML
            const paginationContainer = document.querySelector('.next-pagination') || 
                                       document.querySelector('.pgWrap--RTFKoWa6') ||
                                       document.querySelector('.pagination');
                                       
            if (paginationContainer) {
                console.log('分页容器HTML:', paginationContainer.outerHTML);
                
                // 尝试使用URL方式进行导航
                // 构建请求URL
                const baseUrl = `https://s.taobao.com/search`;
                const targetUrl = `${baseUrl}?page=${pageNum}&q=${encodeURIComponent(keyword)}&sort=sale-desc&tab=all`;
                
                // 使用pushState而不是直接修改location
                if (window.history && window.history.pushState) {
                    window.history.pushState({}, '', targetUrl);
                    window.dispatchEvent(new Event('urlchanged'));
                    console.log(`尝试使用pushState跳转到: ${targetUrl}`);
                    pageClicked = true;
                }
            } else {
                console.log('未找到分页容器');
            }
        }
        
        // 等待URL变化确认分页是否成功
        if (pageClicked) {
            console.log('分页操作已执行,等待URL变化...');
            const originalUrl = window.location.href;
            
            // 等待URL变化或超时
            const urlChangePromise = new Promise((resolve) => {
                let checkCount = 0;
                const maxChecks = 20; // 10秒超时 (500ms * 20)
                
                const checkInterval = setInterval(() => {
                    checkCount++;
                    if (window.location.href !== originalUrl) {
                        clearInterval(checkInterval);
                        console.log(`URL已变化: ${originalUrl} -> ${window.location.href}`);
                        resolve(true);
                    } else if (checkCount >= maxChecks) {
                        clearInterval(checkInterval);
                        console.log(`URL未变化,可能页码未更新,已等待${maxChecks * 500 / 1000}秒`);
                        resolve(false);
                    }
                }, 500);
            });
            
            const urlChanged = await urlChangePromise;
            return urlChanged || pageClicked; // 如果URL变化或按钮点击成功,视为成功
        }
        
        return pageClicked;
    } catch (error) {
        console.error('分页处理出错:', error);
        return false;
    }
}

    // 添加停止按钮
    function addStopButton() {
        if (document.getElementById('stop-btn')) return;
        
        const btnContainer = document.querySelector('#search-btn').parentNode;
        if (!btnContainer) return;
        
        const stopBtn = document.createElement('button');
        stopBtn.id = 'stop-btn';
        stopBtn.textContent = '停止抓取';
        stopBtn.style.cssText = `
            padding: 8px 15px;
            background: #e74c3c;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-top: 10px;
            width: 100%;
        `;
        
        stopBtn.addEventListener('click', () => {
            // 停止抓取过程
            GM_setValue('isScrapingActive', false);
            
            // 更新UI
            document.getElementById('status').textContent = '已手动停止抓取';
            document.getElementById('search-btn').disabled = false;
            
            // 如果已有数据,启用下载按钮
            const items = JSON.parse(GM_getValue('currentItems') || '[]');
            if (items.length > 0) {
                GM_setValue('finalScrapedData', JSON.stringify(items));
                document.getElementById('download-btn').disabled = false;
            }
            
            // 移除停止按钮
            stopBtn.remove();
        });
        
        btnContainer.appendChild(stopBtn);
    }

   // 主函数
function init() {
    console.log('淘宝商品销量抓取工具初始化...');
    
    // 禁用反爬措施
    disableAntiCrawl();
    
    // 设置URL变更监听
    setupUrlChangeListener();
    
    // 创建工具面板
    createToolPanel();
    
    // 绑定开始抓取按钮事件
    document.getElementById('search-btn').addEventListener('click', async () => {
        const keyword = document.getElementById('keyword-input').value;
        const maxPages = parseInt(document.getElementById('max-pages').value) || 3;
        
        if (!keyword) {
            alert('请输入搜索关键词');
            return;
        }
        
        // 清理所有之前的数据
        GM_deleteValue('currentItems');
        GM_deleteValue('currentPage');
        GM_deleteValue('finalScrapedData');
        GM_setValue('isScrapingActive', true);
        GM_setValue('keyword', keyword);
        GM_setValue('maxPages', maxPages);
        
        // 重置UI状态
        document.getElementById('results').innerHTML = '';
        document.getElementById('status').textContent = '准备开始新的抓取...';
        
        // 禁用按钮防止重复点击
        document.getElementById('search-btn').disabled = true;
        document.getElementById('download-btn').disabled = true;
        
        // 执行搜索并获取数据
        await performScraping(keyword, maxPages);
    });
    
    // 绑定下载按钮事件
    document.getElementById('download-btn').addEventListener('click', exportToExcel);
    
    // 重要修改点:不再自动继续抓取,而是立即停止任务并恢复UI状态
    if (GM_getValue('isScrapingActive')) {
        // 检查是否是页面刷新而非正常导航
        const isPageRefresh = performance.navigation && 
                             (performance.navigation.type === 1 || // 标准刷新类型
                              performance.getEntriesByType('navigation')[0]?.type === 'reload'); // 新API
        
        if (isPageRefresh) {
            console.log('检测到页面刷新,停止抓取任务');
            
            // 尝试保存已有数据为最终数据
            const cachedItems = GM_getValue('currentItems');
            if (cachedItems) {
                GM_setValue('finalScrapedData', cachedItems);
                
                try {
                    const data = JSON.parse(cachedItems);
                    const statusElement = document.getElementById('status');
                    if (statusElement && data && data.length > 0) {
                        statusElement.textContent = `抓取已停止,已保存 ${data.length} 条数据`;
                        
                        // 显示数据
                        const resultsElement = document.getElementById('results');
                        if (resultsElement) {
                            displayResults(data, resultsElement);
                        }
                        
                        // 启用下载按钮
                        document.getElementById('download-btn').disabled = false;
                        document.getElementById('search-btn').disabled = false;

                        // 显示通知
                        showNotification(`页面已刷新,抓取已停止,已保存 ${data.length} 条数据`, 'warning');
                    }
                } catch (e) {
                    console.error('解析已保存数据出错:', e);
                }
            }
            
            // 关闭抓取活动状态
            GM_setValue('isScrapingActive', false);
            
            // 恢复关键词和页数设置
            document.getElementById('keyword-input').value = GM_getValue('keyword', '');
            document.getElementById('max-pages').value = GM_getValue('maxPages', 3);
        } else {
            console.log('正常导航,不再继续抓取任务');
            // 关闭抓取活动状态
            GM_setValue('isScrapingActive', false);
        }
    } else {
        // 检查是否有最终数据
        const finalData = GM_getValue('finalScrapedData');
        if (finalData) {
            try {
                const data = JSON.parse(finalData);
                if (data && data.length > 0) {
                    // 恢复UI状态
                    document.getElementById('keyword-input').value = GM_getValue('keyword', '');
                    document.getElementById('max-pages').value = GM_getValue('maxPages', 3);
                    
                    // 启用下载按钮
                    document.getElementById('download-btn').disabled = false;
                    
                    // 更新状态
                    const statusElement = document.getElementById('status');
                    statusElement.textContent = `已加载之前抓取的 ${data.length} 条数据`;
                    
                    // 显示数据
                    const resultsElement = document.getElementById('results');
                    displayResults(data, resultsElement);
                }
            } catch (e) {
                console.error('解析已保存数据出错:', e);
            }
        }
    }
}
// 显示通知提示
function showNotification(message, type = 'info') {
    try {
        // 先检查是否已有通知,如果有则移除
        const existingNotification = document.getElementById('crawler-notification');
        if (existingNotification) {
            existingNotification.remove();
        }
        
        // 创建通知元素
        const notification = document.createElement('div');
        notification.id = 'crawler-notification';
        
        // 设置不同类型的样式
        let backgroundColor = '#3498db'; // 默认信息色
        let icon = 'ℹ️';
        
        if (type === 'success') {
            backgroundColor = '#2ecc71';
            icon = '✅';
        } else if (type === 'warning') {
            backgroundColor = '#f39c12';
            icon = '⚠️';
        } else if (type === 'error') {
            backgroundColor = '#e74c3c';
            icon = '❌';
        }
        
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            padding: 12px 20px;
            background-color: ${backgroundColor};
            color: white;
            border-radius: 4px;
            box-shadow: 0 4px 10px rgba(0,0,0,0.2);
            font-size: 14px;
            font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
            z-index: 10000;
            display: flex;
            align-items: center;
            animation: fadeIn 0.3s ease;
        `;
        
        notification.innerHTML = `
            <span style="margin-right: 8px; font-size: 16px;">${icon}</span>
            <span>${message}</span>
        `;
        
        // 添加到文档
        document.body.appendChild(notification);
        
        // 添加CSS动画
        const style = document.createElement('style');
        style.textContent = `
            @keyframes fadeIn {
                from { opacity: 0; transform: translate(-50%, -10px); }
                to { opacity: 1; transform: translate(-50%, 0); }
            }
        `;
        document.head.appendChild(style);
        
        // 设置自动消失
        setTimeout(() => {
            notification.style.opacity = '0';
            notification.style.transform = 'translate(-50%, -10px)';
            notification.style.transition = 'all 0.3s ease';
            
            setTimeout(() => {
                notification.remove();
                style.remove();
            }, 300);
        }, 3000);
    } catch (error) {
        console.error('显示通知出错:', error);
    }
}

    // 页面加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 1000); // 延迟初始化以确保页面加载完成
    }
    
    // 添加调试信息
    console.log('淘宝商品销量抓取工具已加载');
})();