网页版抖音自动批量取消点赞(喜欢)JS脚本

101

使用教程:

浏览器打开抖音网页版,登陆抖音账号

F12打开控制台,再控制台粘贴脚本

粘贴脚本后执行:unlikeScript.start()

特色功能:

完全随机化睡眠时长和操作间隔

三重验证机制确保取消成功

自适应反拦截策略

智能错误处理和重试

使用帮助:

unlikeScript.stop() - 停止脚本

unlikeScript.start() - 启动脚本

unlikeScript.getStatus() - 查看状态

搬运请保留博客链接,让我的博客有一点流量吧!!!

请不要在抖音控制台执行来路不明的脚本,有可能被盗号!!!

如果有问题请在下方评论区留言,如果有其他需要的脚本也可以留言!

/**
 * 抖音批量取消点赞脚本
 * 作者博客:https://www.zhangzenan.com/
 * 版本:0.42
 */

class DouyinUnlikeScript {
    constructor() {
        this.key = null;
        this.maxCursor = 0;
        this.totalUnliked = 0;
        this.totalVerified = 0;
        this.currentBatch = 0;
        this.isRunning = false;
        this.timer = null;
        this.messageBox = null;
        this.retryCount = 3;
        
        // 原有配置保持不变
        this.config = {
            batchSize: this.getRandomInt(12, 28),
            minDelay: 100,
            maxDelay: 400,
            jitter: 0.3,
            userAgents: [
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15'
            ]
        };
        
        this.init();
    }

    /**
     * 获取随机整数
     */
    getRandomInt(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    /**
     * 获取随机睡眠时长
     */
    getRandomSleep() {
        const baseSleep = this.getRandomInt(this.config.minDelay, this.config.maxDelay);
        const jitter = baseSleep * this.config.jitter;
        return Math.round(baseSleep + this.getRandomInt(-jitter, jitter));
    }

    /**
     * 获取随机User-Agent
     */
    getRandomUserAgent() {
        const randomIndex = this.getRandomInt(0, this.config.userAgents.length - 1);
        return this.config.userAgents[randomIndex];
    }

    /**
     * 检查登录状态
     */
    async checkLoginStatus() {
        try {
            const hasSession = document.cookie.includes('sessionid');
            const hasUserId = document.cookie.includes('user_unique_id');
            const hasSecurityKey = !!localStorage.getItem('security-sdk/s_sdk_cert_key');
            
            return hasSession && hasUserId && hasSecurityKey;
            
        } catch (error) {
            console.error('登录状态检测失败:', error);
            return false;
        }
    }

    /**
     * 获取安全密钥
     */
    async getSecurityKey() {
        try {
            const securityData = localStorage.getItem('security-sdk/s_sdk_cert_key');
            if (!securityData) {
                throw new Error('未找到安全密钥数据');
            }
            
            const { data } = JSON.parse(securityData);
            if (!data) {
                throw new Error('安全密钥格式错误');
            }
            
            return data.replace(/^pub\./, '');
            
        } catch (error) {
            this.showError(`获取密钥失败: ${error.message}`);
            throw error;
        }
    }

    /**
     * 初始化脚本
     */
    async init() {
        try {
            this.showMessage('脚本初始化中...', 'info');
            
            const isLoggedIn = await this.checkLoginStatus();
            if (!isLoggedIn) {
                this.showError('请先登录抖音账号!\n\n登录步骤:\n1. 点击页面右上角头像\n2. 使用手机扫码或验证码登录\n3. 登录成功后重新执行脚本\n4. 如已登陆执行:unlikeScript.start()');
                return;
            }
            
            this.showMessage('✅ 登录状态验证成功', 'success');
            
            this.key = await this.getSecurityKey();
            if (!this.key) {
                throw new Error('安全密钥获取失败');
            }
            
            this.showMessage('✅ 安全密钥获取成功', 'success');
            this.showBlogInfo();
            
            this.start();
            
        } catch (error) {
            this.showError(`初始化失败: ${error.message}`);
            console.error('详细错误:', error);
        }
    }

    /**
     * 显示博客信息
     */
    showBlogInfo() {
        try {
            const blogBox = document.createElement('div');
            blogBox.id = 'douyin-blog-info';
            blogBox.style.cssText = `
                position: fixed !important;
                top: 50% !important;
                left: 50% !important;
                transform: translate(-50%, -50%) !important;
                background: linear-gradient(135deg, #2196F3 0%, #64B5F6 100%) !important;
                color: white !important;
                padding: 20px 25px !important;
                border-radius: 15px !important;
                box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3) !important;
                font-family: Arial, sans-serif !important;
                font-size: 14px !important;
                text-align: center !important;
                z-index: 9999999 !important;
                border: 2px solid rgba(255, 255, 255, 0.2) !important;
                max-width: 350px !important;
                width: 90% !important;
                opacity: 1 !important;
                visibility: visible !important;
            `;

            blogBox.innerHTML = `
                <div style="margin-bottom: 10px; font-size: 16px; font-weight: bold;">
                    💻 作者技术博客
                </div>
                <div style="margin-bottom: 10px; font-size: 12px; line-height: 1.4;">
                    欢迎访问获取更多技术干货和实用脚本!
                </div>
                <a href="https://www.zhangzenan.com/" target="_blank" rel="noopener noreferrer"
                   style="color: #FFD700; text-decoration: none; font-weight: bold; font-size: 14px;">
                    zhangzenan.com
                </a>
                <div style="margin-top: 15px;">
                    <button onclick="this.parentElement.parentElement.remove();"
                            style="padding: 6px 15px; background: rgba(255,255,255,0.2); color: white; border: none; border-radius: 15px; cursor: pointer; font-size: 12px;">
                        关闭
                    </button>
                </div>
            `;

            document.body.appendChild(blogBox);

            setTimeout(() => {
                const closeHandler = (e) => {
                    if (!blogBox.contains(e.target)) {
                        blogBox.remove();
                        document.removeEventListener('click', closeHandler);
                    }
                };
                document.addEventListener('click', closeHandler);
            }, 500);

            setTimeout(() => {
                if (document.getElementById('douyin-blog-info')) {
                    blogBox.style.opacity = '0';
                    setTimeout(() => {
                        if (document.getElementById('douyin-blog-info')) {
                            blogBox.remove();
                        }
                    }, 300);
                }
            }, 6000);

        } catch (error) {
            console.log('博客信息显示失败,在控制台显示:', error);
            console.log('📝 作者技术博客:https://www.zhangzenan.com/');
        }
    }

    /**
     * 开始执行脚本
     */
    start() {
        if (this.isRunning) {
            this.showMessage('脚本已经在运行中', 'warning');
            return;
        }

        this.isRunning = true;
        this.showMessage('🚀 脚本开始运行...', 'success');
        
        this.processBatch();
        
        const batchInterval = this.getRandomInt(3000, 7000);
        this.timer = setInterval(() => this.processBatch(), batchInterval);
        
        this.showMessage(`⏰ 批次间隔设置为 ${batchInterval}ms`, 'info');
    }

    /**
     * 停止脚本
     */
    stop() {
        this.isRunning = false;
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
        }
        
        const successRate = this.totalUnliked > 0 ? Math.round((this.totalVerified / this.totalUnliked) * 100) : 0;
        const finalMessage = `🛑 脚本已停止。\n📊 统计结果:\n   • 总计取消:${this.totalUnliked}个\n   • 验证通过:${this.totalVerified}个\n   • 成功率:${successRate}%`;
        
        this.showMessage(finalMessage, 'info');
    }

    /**
     * 获取请求头
     */
    getHeaders() {
        return {
            'Accept': 'application/json, text/plain, */*',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Referer': 'https://www.douyin.com/',
            'User-Agent': this.getRandomUserAgent(),
            'bd-ticket-guard-ree-public-key': this.key,
            'X-Requested-With': 'XMLHttpRequest',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Connection': 'keep-alive',
            'Origin': 'https://www.douyin.com'
        };
    }

    /**
     * 处理一批点赞
     */
    async processBatch() {
        if (!this.isRunning) return;

        try {
            this.currentBatch++;
            this.showMessage(`📦 正在处理第${this.currentBatch}批数据...`, 'info');
            
            const { awemeList, nextCursor, hasMore } = await this.getLikedVideos();
            
            if (!awemeList || awemeList.length === 0) {
                this.showMessage('🎉 没有更多点赞视频,任务完成!', 'success');
                this.stop();
                return;
            }

            const batchSize = this.getRandomInt(10, Math.min(this.config.batchSize, awemeList.length));
            const batchToProcess = awemeList.slice(0, batchSize);
            
            this.showMessage(`📋 第${this.currentBatch}批找到${awemeList.length}个视频,本次处理${batchSize}个`, 'info');
            
            await this.processVideos(batchToProcess);
            
            this.maxCursor = nextCursor;
            
            if (!hasMore) {
                this.showMessage('🏁 已处理完所有点赞视频', 'success');
                this.stop();
            }

        } catch (error) {
            this.showError(`❌ 批次处理失败: ${error.message}`);
        }
    }

    /**
     * 处理视频列表
     */
    async processVideos(videos) {
        const results = [];
        
        for (let i = 0; i < videos.length; i++) {
            if (!this.isRunning) break;
            
            const video = videos[i];
            const progress = `${i + 1}/${videos.length}`;
            
            try {
                this.showMessage(`🔄 正在处理第${progress}个视频...`, 'info');
                
                // 使用修改后的重试函数
                const cancelSuccess = await this.retryWithSpecificDelay(
                    () => this.cancelLike(video.aweme_id),
                    this.retryCount,
                    `取消第${progress}个视频`
                );
                
                if (!cancelSuccess) {
                    this.showError(`❌ 第${progress}个视频取消失败,跳过`);
                    results.push(false);
                    await this.sleep(this.getRandomSleep() * 2);
                    continue;
                }
                
                this.showMessage(`🔍 正在验证第${progress}个视频...`, 'info');
                const verifySuccess = await this.verifyUnlike(video.aweme_id);
                
                if (verifySuccess) {
                    this.totalUnliked++;
                    this.totalVerified++;
                    this.showMessage(
                        `✅ 第${progress}个视频处理成功!总计:${this.totalUnliked}个`, 
                        'success'
                    );
                } else {
                    this.totalUnliked++;
                    this.showMessage(
                        `⚠️ 第${progress}个视频取消成功,验证超时!总计:${this.totalUnliked}个`, 
                        'warning'
                    );
                }
                
                results.push(true);
                
            } catch (error) {
                this.showError(`❌ 第${progress}个视频处理异常: ${error.message}`);
                results.push(false);
            }
            
            await this.sleep(this.getRandomSleep());
        }

        const successCount = results.filter(Boolean).length;
        const successRate = videos.length > 0 ? Math.round((successCount / videos.length) * 100) : 0;
        
        this.showMessage(
            `📊 本批处理完成:成功${successCount}/${videos.length}个 (${successRate}%)\n   累计:${this.totalUnliked}个,验证通过:${this.totalVerified}个`,
            'success'
        );

        return results;
    }

    /**
     *  修改:带特定错误处理的重试机制
     */
    async retryWithSpecificDelay(operation, maxRetries, operationName = '操作') {
        for (let i = 0; i < maxRetries; i++) {
            try {
                const result = await operation();
                if (result) return true;
                
            } catch (error) {
                const errorMsg = error.message || '';
                
                //  只针对这个特定错误添加1-5秒随机延时
                const specificError = '点赞速度太快啦,休息一会儿再使用吧';
                if (errorMsg === specificError) {
                    const randomDelay = Math.floor(Math.random() * 4000) + 1000; // 1-5秒
                    this.showMessage(`⚠️ 检测到特定错误:"${specificError}"`, 'error');
                    this.showMessage(`⏰ 执行1-5秒随机延时:${randomDelay/1000}秒`, 'info');
                    
                    // 执行随机延时
                    await this.sleep(randomDelay);
                    continue;
                }
                
                // 其他错误的原有处理逻辑保持不变
                this.showMessage(`⚠️ ${operationName}重试 ${i + 1}/${maxRetries} 失败: ${errorMsg}`, 'warning');
                
                if (i < maxRetries - 1) {
                    const retryDelay = this.getRandomSleep() * (i + 1);
                    await this.sleep(retryDelay);
                }
            }
        }
        return false;
    }

    /**
     * 获取点赞视频列表
     */
    async getLikedVideos() {
        try {
            const response = await fetch(
                `https://www.douyin.com/aweme/v1/web/aweme/favorite?aid=6383&count=999&max_cursor=${this.maxCursor}`,
                {
                    method: 'GET',
                    credentials: 'include',
                    headers: this.getHeaders()
                }
            );

            if (!response.ok) {
                if (response.status === 401) {
                    throw new Error('未登录或登录状态失效');
                } else if (response.status === 429) {
                    throw new Error('请求过于频繁,请稍后再试');
                }
                throw new Error(`HTTP错误: ${response.status}`);
            }

            const data = await response.json();
            
            if (data.status_code === 100005) {
                throw new Error('需要登录');
            }
            
            return {
                awemeList: data.aweme_list || [],
                nextCursor: data.max_cursor || 0,
                hasMore: data.has_more === 1
            };

        } catch (error) {
            throw new Error(`获取点赞列表失败: ${error.message}`);
        }
    }

    /**
     * 取消单个点赞
     */
    async cancelLike(awemeId) {
        try {
            const response = await fetch(
                'https://www.douyin.com/aweme/v1/web/commit/item/digg/?aid=6383',
                {
                    method: 'POST',
                    credentials: 'include',
                    headers: this.getHeaders(),
                    body: `aweme_id=${awemeId}&item_type=0&type=0`
                }
            );

            const data = await response.json();
            
            if (data.status_code !== 0) {
                const errorMsg = data.status_msg || '取消点赞失败';
                throw new Error(errorMsg);
            }

            return true;

        } catch (error) {
            throw error;
        }
    }

    /**
     * 验证取消点赞结果
     */
    async verifyUnlike(awemeId) {
        try {
            await this.sleep(this.getRandomInt(800, 2000));
            
            const detailVerified = await this.verifyViaDetail(awemeId);
            if (detailVerified) {
                return true;
            }
            
            const listVerified = await this.verifyViaList(awemeId);
            return listVerified;

        } catch (error) {
            this.showError(`⚠️ 验证失败: ${error.message}`);
            return false;
        }
    }

    /**
     * 通过视频详情验证
     */
    async verifyViaDetail(awemeId) {
        try {
            const response = await fetch(
                `https://www.douyin.com/aweme/v1/web/aweme/detail/?aid=6383&aweme_id=${awemeId}`,
                {
                    method: 'GET',
                    credentials: 'include',
                    headers: this.getHeaders()
                }
            );

            if (!response.ok) return false;

            const data = await response.json();
            return data.aweme_detail?.user_digged === 0;

        } catch (error) {
            console.log('详情验证失败:', error.message);
            return false;
        }
    }

    /**
     * 通过点赞列表验证
     */
    async verifyViaList(awemeId) {
        try {
            const response = await fetch(
                `https://www.douyin.com/aweme/v1/web/aweme/favorite?aid=6383&count=50&max_cursor=0`,
                {
                    method: 'GET',
                    credentials: 'include',
                    headers: this.getHeaders()
                }
            );

            if (!response.ok) return false;

            const data = await response.json();
            const isLiked = data.aweme_list?.some(item => item.aweme_id === awemeId);
            return !isLiked;

        } catch (error) {
            console.log('列表验证失败:', error.message);
            return false;
        }
    }

    /**
     * 显示消息
     */
    showMessage(text, type = 'info') {
        const timestamp = new Date().toLocaleTimeString();
        console.log(`[${timestamp}] ${text}`);
        
        try {
            if (!this.messageBox) {
                this.createMessageBox();
            }

            const colors = {
                info: '#2196F3',
                success: '#4CAF50',
                error: '#F44336',
                warning: '#FF9800'
            };

            this.messageBox.style.backgroundColor = colors[type] || colors.info;
            this.messageBox.textContent = `[${timestamp}] ${text}`;
            
        } catch (error) {
            console.error('消息显示失败:', error);
        }
    }

    /**
     * 显示错误消息
     */
    showError(text) {
        this.showMessage(text, 'error');
    }

    /**
     * 创建消息框
     */
    createMessageBox() {
        try {
            this.messageBox = document.createElement('div');
            this.messageBox.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                background: #2196F3;
                color: white;
                padding: 15px 20px;
                border-radius: 12px;
                box-shadow: 0 6px 20px rgba(0,0,0,0.2);
                font-family: Arial, sans-serif;
                font-size: 14px;
                max-width: 500px;
                z-index: 999999;
                word-wrap: break-word;
                max-height: 300px;
                overflow-y: auto;
            `;

            document.body.appendChild(this.messageBox);

            this.messageBox.addEventListener('dblclick', () => {
                this.messageBox.remove();
                this.messageBox = null;
            });

        } catch (error) {
            console.error('消息框创建失败:', error);
            this.messageBox = null;
        }
    }

    /**
     * 睡眠函数
     */
    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * 获取脚本状态
     */
    getStatus() {
        return {
            isRunning: this.isRunning,
            totalUnliked: this.totalUnliked,
            totalVerified: this.totalVerified,
            currentBatch: this.currentBatch,
            maxCursor: this.maxCursor,
            config: this.config
        };
    }
}

// 初始化脚本
let unlikeScript;

function initScript() {
    try {
        unlikeScript = new DouyinUnlikeScript();
        window.unlikeScript = unlikeScript;
        
        console.log('='.repeat(70));
        console.log('🎯 抖音批量取消点赞脚本');
        console.log('👤 作者博客:https://www.zhangzenan.com/');
        console.log('='.repeat(70));
        console.log('='.repeat(70));
        console.log('📚 使用帮助:');
        console.log('   unlikeScript.stop()    - 停止脚本');
        console.log('   unlikeScript.start()   - 启动脚本');
        console.log('   unlikeScript.getStatus() - 查看状态');
        console.log('='.repeat(70));

    } catch (error) {
        console.error('❌ 脚本初始化失败:', error.message);
        alert(`⚠️ 脚本初始化失败:${error.message}\n\n请确保:\n1. 已登录抖音账号\n2. 网络连接正常\n3. 刷新页面后重试\n4. 如已登陆执行:unlikeScript.start()`);
    }
}

// 等待页面完全加载后初始化
if (document.readyState === 'complete') {
    initScript();
} else {
    window.addEventListener('load', initScript);
    setTimeout(initScript, 5000);
}

说谢谢了吗?