您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
提取抖音用户视频链接,支持正常提取和排序提取功能
// ==UserScript== // @name 抖音视频提取器(简洁版) // @namespace http://tampermonkey.net/ // @version 1.1.8 // @description 提取抖音用户视频链接,支持正常提取和排序提取功能 // @author qqlcx5 // @match https://www.douyin.com/user/* // @match https://www.douyin.com/search/* // @icon https://www.google.com/s2/favicons?sz=64&domain=douyin.com // @grant GM_setClipboard // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; /** * 存储提取到的视频链接和点赞数 * Stores the extracted video links and like counts */ let videoLinks = []; /** * 提取用户主页中的所有视频链接和点赞数 * Extracts all video links and like counts from the user's profile page */ function extractVideoLinks() { // 定义需要提取链接的节点选择器 const selectors = [ 'div[data-e2e="user-post-list"]', 'div[data-e2e="user-like-list"]' ]; const videoListContainer = document.querySelector(selectors); if (!videoListContainer) { console.warn('未找到视频列表元素'); return; } const videoAnchorElements = videoListContainer.querySelectorAll('a[href^="/video/"]'); videoLinks = Array.from(videoAnchorElements).map(anchor => { const videoElement = anchor.closest('li'); const likeCountElement = videoElement ? videoElement.querySelector('.b3Dh2ia8') : null; const likeCount = likeCountElement ? parseLikeCount(likeCountElement.textContent) : 0; const url = new URL(anchor.href); url.searchParams.set('likeCount', likeCount); return { href: url.toString(), likeCount: likeCount }; }); console.info(`提取到 ${videoLinks.length} 个视频链接`); } /** * 将点赞数文本转换为数字 * Converts like count text to a number * @param {string} text - 点赞数文本 (Like count text) * @returns {number} - 转换后的点赞数 (Converted like count) */ function parseLikeCount(text) { if (text.includes('万')) { return parseFloat(text) * 10000; } return parseInt(text, 10); } /** * 按点赞数排序视频链接 * Sorts video links by like count * @param {boolean} ascending - 是否升序排序 (Whether to sort in ascending order) */ function sortVideoLinksByLikes(ascending = false) { videoLinks.sort((a, b) => { return ascending ? a.likeCount - b.likeCount : b.likeCount - a.likeCount; }); } /** * 复制所有视频链接到剪贴板 * Copies all video links to the clipboard * @param {boolean} shouldSort - 是否按点赞数排序 (Whether to sort by like count) */ function copyAllVideoLinks(shouldSort = false) { extractVideoLinks(); if (videoLinks.length === 0) { showNotification('未找到视频链接', 'error'); return; } if (shouldSort) { sortVideoLinksByLikes(); showNotification('已按点赞数降序排序', 'info'); } const linksText = videoLinks.map(video => video.href).join('\n'); GM_setClipboard(linksText); showNotification(`已复制 ${videoLinks.length} 个视频链接`, 'success'); } /** * 创建并添加悬浮按钮组到页面 * Creates and adds a floating button group to the page */ function createFloatingButtonGroup() { const buttonGroup = document.createElement('div'); buttonGroup.id = 'floating-button-group'; Object.assign(buttonGroup.style, { position: 'fixed', right: '24px', bottom: '24px', display: 'flex', flexDirection: 'column', gap: '12px', zIndex: '10000', }); const normalButton = createButton('提取链接', '#007AFF', () => { copyAllVideoLinks(false); }); // 排序提取按钮 const sortButton = createButton('点赞数提取', '#FF4D4F', () => { copyAllVideoLinks(true); // 排序 }); // 添加按钮到按钮组 buttonGroup.appendChild(normalButton); buttonGroup.appendChild(sortButton); // 添加按钮组到页面主体 document.body.appendChild(buttonGroup); } /** * 创建按钮 * Creates a button * @param {string} text - 按钮文字 (Button text) * @param {string} color - 按钮背景色 (Button background color) * @param {function} onClick - 点击事件 (Click event) * @returns {HTMLElement} - 按钮元素 (Button element) */ function createButton(text, color, onClick) { const button = document.createElement('button'); button.textContent = text; Object.assign(button.style, { padding: '12px 24px', backgroundColor: color, color: '#FFFFFF', border: 'none', borderRadius: '12px', boxShadow: '0 2px 8px rgba(0, 122, 255, 0.15)', cursor: 'pointer', fontSize: '15px', fontWeight: '500', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', WebkitAppearance: 'none', margin: '0', userSelect: 'none', WebkitTapHighlightColor: 'transparent' }); button.addEventListener('mouseenter', () => { button.style.transform = 'scale(1.02) translateY(-1px)'; button.style.boxShadow = '0 4px 12px rgba(0, 122, 255, 0.2)'; button.style.backgroundColor = '#0066D6'; }); button.addEventListener('mouseleave', () => { button.style.transform = 'none'; button.style.boxShadow = '0 2px 8px rgba(0, 122, 255, 0.15)'; button.style.backgroundColor = color; }); button.addEventListener('mousedown', () => { button.style.transform = 'scale(0.98)'; }); button.addEventListener('mouseup', () => { button.style.transform = 'scale(1.02) translateY(-1px)'; }); button.addEventListener('click', onClick); return button; } function showNotification(message, type = 'info') { const colors = { success: '#34C759', error: '#FF3B30', info: '#007AFF' }; const notification = document.createElement('div'); notification.textContent = message; Object.assign(notification.style, { position: 'fixed', bottom: '90px', right: '24px', backgroundColor: '#FFFFFF', color: colors[type], padding: '12px 20px', borderRadius: '12px', boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)', opacity: '0', transform: 'translateY(10px)', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', zIndex: '10000', fontSize: '15px', fontWeight: '500', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', border: `1px solid ${colors[type]}20` }); document.body.appendChild(notification); requestAnimationFrame(() => { notification.style.opacity = '1'; notification.style.transform = 'translateY(0)'; }); setTimeout(() => { notification.style.opacity = '0'; notification.style.transform = 'translateY(10px)'; setTimeout(() => { document.body.removeChild(notification); }, 300); }, 3000); } function initializeScript() { createFloatingButtonGroup(); console.info('抖音视频链接提取器已启用'); } if (document.readyState === 'complete' || document.readyState === 'interactive') { initializeScript(); } else { document.addEventListener('DOMContentLoaded', initializeScript); } })();