您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
提取YouTube频道所有公开视频的链接
// ==UserScript== // @name YouTube视频链接提取器 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 提取YouTube频道所有公开视频的链接 // @author huayuhuia // @match https://www.youtube.com/* // @match https://youtube.com/* // @grant none // @license MIT // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // 样式 const styles = ` .yt-extractor-btn { position: fixed; top: 70px; right: 20px; background-color: red; color: white; border: none; border-radius: 4px; padding: 8px 12px; font-size: 14px; cursor: pointer; z-index: 9999; font-weight: bold; } .yt-extractor-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); width: 80%; max-width: 800px; max-height: 80vh; z-index: 10000; display: flex; flex-direction: column; } .yt-extractor-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid #e0e0e0; } .yt-extractor-title { font-size: 18px; font-weight: bold; margin: 0; } .yt-extractor-close { background: none; border: none; font-size: 20px; cursor: pointer; color: #606060; } .yt-extractor-content { padding: 16px; overflow-y: auto; flex-grow: 1; } .yt-extractor-footer { padding: 12px 16px; border-top: 1px solid #e0e0e0; display: flex; justify-content: space-between; } .yt-extractor-status { color: #606060; } .yt-extractor-actions button { margin-left: 8px; padding: 6px 12px; border-radius: 4px; border: none; cursor: pointer; } .yt-extractor-copy { background-color: #065fd4; color: white; } .yt-extractor-download { background-color: #2ba640; color: white; } .yt-extractor-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 9999; } .yt-extractor-progress { width: 100%; height: 4px; background-color: #f0f0f0; margin-top: 8px; } .yt-extractor-progress-bar { height: 100%; background-color: red; width: 0%; transition: width 0.3s; } .yt-extractor-video-list { list-style: none; padding: 0; margin: 0; } .yt-extractor-video-item { padding: 8px 0; border-bottom: 1px solid #f0f0f0; } .yt-extractor-video-item:last-child { border-bottom: none; } .yt-extractor-video-title { font-weight: bold; margin-bottom: 4px; } .yt-extractor-video-link { color: #065fd4; text-decoration: none; word-break: break-all; } .yt-extractor-video-meta { font-size: 12px; color: #606060; margin-top: 4px; } `; // 添加样式 const styleElement = document.createElement('style'); styleElement.textContent = styles; document.head.appendChild(styleElement); // 创建提取按钮 function createExtractorButton() { const button = document.createElement('button'); button.className = 'yt-extractor-btn'; button.textContent = '提取视频链接'; button.addEventListener('click', startExtraction); document.body.appendChild(button); } // 创建模态框 function createModal() { const overlay = document.createElement('div'); overlay.className = 'yt-extractor-overlay'; const modal = document.createElement('div'); modal.className = 'yt-extractor-modal'; const header = document.createElement('div'); header.className = 'yt-extractor-header'; const title = document.createElement('h2'); title.className = 'yt-extractor-title'; title.textContent = '视频链接提取器'; const closeBtn = document.createElement('button'); closeBtn.className = 'yt-extractor-close'; closeBtn.textContent = '×'; closeBtn.addEventListener('click', () => { document.body.removeChild(overlay); }); header.appendChild(title); header.appendChild(closeBtn); const content = document.createElement('div'); content.className = 'yt-extractor-content'; const statusDiv = document.createElement('div'); statusDiv.className = 'yt-extractor-status'; statusDiv.textContent = '准备提取视频链接...'; const progressContainer = document.createElement('div'); progressContainer.className = 'yt-extractor-progress'; const progressBar = document.createElement('div'); progressBar.className = 'yt-extractor-progress-bar'; progressContainer.appendChild(progressBar); const videoList = document.createElement('ul'); videoList.className = 'yt-extractor-video-list'; content.appendChild(statusDiv); content.appendChild(progressContainer); content.appendChild(videoList); const footer = document.createElement('div'); footer.className = 'yt-extractor-footer'; const footerStatus = document.createElement('div'); footerStatus.className = 'yt-extractor-status'; footerStatus.textContent = '视频数量: 0'; const actions = document.createElement('div'); actions.className = 'yt-extractor-actions'; const copyBtn = document.createElement('button'); copyBtn.className = 'yt-extractor-copy'; copyBtn.textContent = '复制链接'; copyBtn.addEventListener('click', () => { copyToClipboard(videoList); }); const downloadBtn = document.createElement('button'); downloadBtn.className = 'yt-extractor-download'; downloadBtn.textContent = '下载TXT'; downloadBtn.addEventListener('click', () => { downloadAsTxt(videoList); }); actions.appendChild(copyBtn); actions.appendChild(downloadBtn); footer.appendChild(footerStatus); footer.appendChild(actions); modal.appendChild(header); modal.appendChild(content); modal.appendChild(footer); overlay.appendChild(modal); document.body.appendChild(overlay); return { overlay, modal, statusDiv, progressBar, videoList, footerStatus }; } // 复制到剪贴板 function copyToClipboard(videoList) { const links = []; const items = videoList.querySelectorAll('.yt-extractor-video-item'); items.forEach(item => { const title = item.querySelector('.yt-extractor-video-title').textContent; const link = item.querySelector('.yt-extractor-video-link').href; links.push(`${title}\n${link}\n`); }); const text = links.join('\n'); navigator.clipboard.writeText(text).then(() => { alert('链接已复制到剪贴板!'); }).catch(err => { console.error('复制失败:', err); alert('复制失败,请手动复制。'); }); } // 下载为TXT function downloadAsTxt(videoList) { const links = []; const items = videoList.querySelectorAll('.yt-extractor-video-item'); items.forEach(item => { const title = item.querySelector('.yt-extractor-video-title').textContent; const link = item.querySelector('.yt-extractor-video-link').href; const meta = item.querySelector('.yt-extractor-video-meta')?.textContent || ''; links.push(`标题: ${title}\n链接: ${link}\n${meta}\n${'='.repeat(80)}\n`); }); const text = links.join('\n'); const channelName = getChannelName(); const blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `youtube_videos_${channelName}_${new Date().toISOString().slice(0, 10)}.txt`; a.click(); URL.revokeObjectURL(url); } // 获取频道名称 function getChannelName() { const url = window.location.href; const match = url.match(/\/@([^\/]+)/); if (match) { return match[1]; } // 尝试从页面获取频道名称 const channelNameElement = document.querySelector('#channel-name'); if (channelNameElement) { return channelNameElement.textContent.trim().replace(/\s+/g, '_'); } return 'channel'; } // 检查是否在频道页面 function isChannelPage() { return window.location.href.includes('/channel/') || window.location.href.includes('/@') || window.location.href.includes('/c/') || window.location.href.includes('/user/'); } // 检查是否在视频页面 function isVideosTab() { return window.location.href.includes('/videos'); } // 开始提取 function startExtraction() { if (!isChannelPage()) { alert('请在YouTube频道页面使用此功能!'); return; } if (!isVideosTab()) { // 如果不在视频标签页,跳转到视频标签页 const currentUrl = window.location.href; const videosUrl = currentUrl.endsWith('/') ? `${currentUrl}videos` : `${currentUrl}/videos`; window.location.href = videosUrl; return; } const modalElements = createModal(); // 开始提取视频 extractVideos(modalElements); } // 提取视频 async function extractVideos(modalElements) { const { statusDiv, progressBar, videoList, footerStatus } = modalElements; statusDiv.textContent = '正在提取视频链接...'; const videos = []; let lastHeight = 0; let scrollAttempts = 0; const maxScrollAttempts = 300; // 最大滚动次数 while (scrollAttempts < maxScrollAttempts) { // 滚动到页面底部 window.scrollTo(0, document.documentElement.scrollHeight); // 等待内容加载 await new Promise(resolve => setTimeout(resolve, 1500)); // 提取当前页面上的视频 const currentVideos = extractCurrentVideos(); // 更新视频列表 for (const video of currentVideos) { if (!videos.some(v => v.id === video.id)) { videos.push(video); // 添加到UI const item = document.createElement('li'); item.className = 'yt-extractor-video-item'; const title = document.createElement('div'); title.className = 'yt-extractor-video-title'; title.textContent = video.title; const link = document.createElement('a'); link.className = 'yt-extractor-video-link'; link.href = video.url; link.textContent = video.url; link.target = '_blank'; const meta = document.createElement('div'); meta.className = 'yt-extractor-video-meta'; if (video.published || video.views) { meta.textContent = [video.published, video.views].filter(Boolean).join(' • '); } item.appendChild(title); item.appendChild(link); item.appendChild(meta); videoList.appendChild(item); } } // 更新状态 statusDiv.textContent = `已找到 ${videos.length} 个视频,继续滚动加载更多...`; footerStatus.textContent = `视频数量: ${videos.length}`; // 检查是否已经到达底部 const currentHeight = document.documentElement.scrollHeight; if (currentHeight === lastHeight) { scrollAttempts++; if (scrollAttempts >= 3) { // 连续3次没有新内容,认为已到达底部 break; } } else { scrollAttempts = 0; lastHeight = currentHeight; } // 更新进度条 progressBar.style.width = `${Math.min((scrollAttempts / 3) * 100, 100)}%`; } // 完成提取 statusDiv.textContent = `提取完成!共找到 ${videos.length} 个视频。`; progressBar.style.width = '100%'; } // 从当前页面提取视频 function extractCurrentVideos() { const videos = []; console.log('开始提取视频...'); // 尝试多种可能的视频容器选择器 const selectors = [ 'ytd-grid-video-renderer', 'ytd-rich-item-renderer', 'ytd-video-renderer', 'ytd-compact-video-renderer', 'ytd-reel-item-renderer', // 更通用的选择器 'div[id="dismissible"]', 'div[class*="ytd-rich-grid-media"]', 'div[class*="ytd-grid-video"]' ]; // 使用更通用的选择器组合 const selectorString = selectors.join(', '); console.log(`使用选择器: ${selectorString}`); const videoElements = document.querySelectorAll(selectorString); console.log(`找到 ${videoElements.length} 个可能的视频元素`); // 遍历所有找到的元素 videoElements.forEach((element, index) => { try { console.log(`处理第 ${index + 1} 个元素...`); // 尝试多种可能的链接选择器 const linkSelectors = [ 'a#video-title', 'a.yt-simple-endpoint.style-scope.ytd-video-renderer', 'a[href*="watch?v="]', 'a[title]', 'a[aria-label]' ]; // 尝试每个选择器直到找到有效的链接元素 let linkElement = null; for (const selector of linkSelectors) { const el = element.querySelector(selector); if (el && el.href && el.href.includes('watch?v=')) { linkElement = el; console.log(`找到链接元素,使用选择器: ${selector}`); break; } } // 如果没有找到链接元素,尝试查找任何带有href的a标签 if (!linkElement) { const allLinks = element.querySelectorAll('a[href]'); for (const link of allLinks) { if (link.href && link.href.includes('watch?v=')) { linkElement = link; console.log('找到备用链接元素'); break; } } } if (!linkElement) { console.log('未找到有效的链接元素,跳过'); return; } const videoUrl = linkElement.href; if (!videoUrl || !videoUrl.includes('watch?v=')) { console.log('链接不是有效的视频URL,跳过'); return; } // 提取视频ID const videoId = new URL(videoUrl).searchParams.get('v'); if (!videoId) { console.log('无法提取视频ID,跳过'); return; } // 提取标题 - 尝试多种方法 let title = ''; if (linkElement.getAttribute('title')) { title = linkElement.getAttribute('title'); } else if (linkElement.getAttribute('aria-label')) { title = linkElement.getAttribute('aria-label'); } else if (linkElement.textContent.trim()) { title = linkElement.textContent.trim(); } else { // 尝试从父元素中查找标题 const possibleTitleElements = element.querySelectorAll('h3, .title, [id*="title"]'); for (const el of possibleTitleElements) { if (el.textContent.trim()) { title = el.textContent.trim(); break; } } } if (!title) { title = `视频 ${videoId}`; } // 提取发布时间和观看次数 - 尝试多种方法 let published = ''; let views = ''; // 1. 尝试标准元数据选择器 const metaSelectors = [ '#metadata-line span', '.metadata-line span', '#metadata span', '.ytd-video-meta-block span', '[id*="metadata"] span', '[class*="metadata"] span' ]; for (const selector of metaSelectors) { const metaElements = element.querySelectorAll(selector); if (metaElements.length >= 2) { views = metaElements[0].textContent.trim(); published = metaElements[1].textContent.trim(); break; } else if (metaElements.length === 1) { views = metaElements[0].textContent.trim(); break; } } // 2. 如果上面的方法失败,使用正则表达式从元素文本中提取 if (!views && !published) { const infoText = element.textContent; // 匹配观看次数 const viewsPatterns = [ /(\d+(\.\d+)?[KMB]?次观看)/, /(\d+(\.\d+)?[KMB]? views)/, /(\d+(\.\d+)?[KMB]?播放)/, /观看次数:(\d+(\.\d+)?[KMB]?)/ ]; for (const pattern of viewsPatterns) { const match = infoText.match(pattern); if (match) { views = match[0]; break; } } // 匹配发布时间 const timePatterns = [ /((\d+)天前|(\d+)小时前|(\d+)分钟前|(\d+)秒前)/, /((\d+) days ago|(\d+) hours ago|(\d+) minutes ago|(\d+) seconds ago)/, /((\d+)天|(\d+)小时|(\d+)分钟|(\d+)秒)/ ]; for (const pattern of timePatterns) { const match = infoText.match(pattern); if (match) { published = match[0]; break; } } } // 添加视频到列表 videos.push({ id: videoId, title: title, url: videoUrl, published: published, views: views }); console.log(`成功提取视频: ${title} (${videoId})`); } catch (e) { console.error('提取视频信息时出错:', e); } }); console.log(`本次提取到 ${videos.length} 个视频`); return videos; } // 修改提取视频函数,增加延迟和重试机制 async function extractVideos(modalElements) { const { statusDiv, progressBar, videoList, footerStatus } = modalElements; statusDiv.textContent = '正在提取视频链接...'; const videos = []; let lastHeight = 0; let scrollAttempts = 0; let emptyAttempts = 0; const maxScrollAttempts = 300; // 最大滚动次数 const maxEmptyAttempts = 5; // 最大空提取次数 while (scrollAttempts < maxScrollAttempts && emptyAttempts < maxEmptyAttempts) { // 滚动到页面底部 window.scrollTo(0, document.documentElement.scrollHeight); // 等待内容加载 - 增加等待时间 await new Promise(resolve => setTimeout(resolve, 2000)); // 提取当前页面上的视频 const currentVideos = extractCurrentVideos(); if (currentVideos.length === 0) { emptyAttempts++; console.log(`未找到视频,空提取尝试 ${emptyAttempts}/${maxEmptyAttempts}`); statusDiv.textContent = `正在加载视频,请稍候... (${emptyAttempts}/${maxEmptyAttempts})`; } else { emptyAttempts = 0; // 重置空提取计数 } // 更新视频列表 for (const video of currentVideos) { if (!videos.some(v => v.id === video.id)) { videos.push(video); // 添加到UI const item = document.createElement('li'); item.className = 'yt-extractor-video-item'; const title = document.createElement('div'); title.className = 'yt-extractor-video-title'; title.textContent = video.title; const link = document.createElement('a'); link.className = 'yt-extractor-video-link'; link.href = video.url; link.textContent = video.url; link.target = '_blank'; const meta = document.createElement('div'); meta.className = 'yt-extractor-video-meta'; if (video.published || video.views) { meta.textContent = [video.published, video.views].filter(Boolean).join(' • '); } item.appendChild(title); item.appendChild(link); item.appendChild(meta); videoList.appendChild(item); } } // 更新状态 statusDiv.textContent = `已找到 ${videos.length} 个视频,继续滚动加载更多...`; footerStatus.textContent = `视频数量: ${videos.length}`; // 检查是否已经到达底部 const currentHeight = document.documentElement.scrollHeight; if (currentHeight === lastHeight) { scrollAttempts++; if (scrollAttempts >= 3) { // 连续3次没有新内容,认为已到达底部 break; } } else { scrollAttempts = 0; lastHeight = currentHeight; } // 更新进度条 progressBar.style.width = `${Math.min((scrollAttempts / 3) * 100, 100)}%`; } // 完成提取 if (videos.length > 0) { statusDiv.textContent = `提取完成!共找到 ${videos.length} 个视频。`; } else { statusDiv.textContent = `未能找到视频。请尝试刷新页面或检查控制台日志。`; } progressBar.style.width = '100%'; } // 复制到剪贴板 function copyToClipboard(videoList) { const links = []; const items = videoList.querySelectorAll('.yt-extractor-video-item'); console.log(`准备复制 ${items.length} 个视频链接`); // 添加调试信息 items.forEach(item => { const title = item.querySelector('.yt-extractor-video-title').textContent; const link = item.querySelector('.yt-extractor-video-link').href; links.push(`${title}\n${link}\n`); }); const text = links.join('\n'); if (text.trim() === '') { alert('没有找到视频链接可复制!'); return; } // 使用备用方法复制到剪贴板 try { navigator.clipboard.writeText(text).then(() => { alert('链接已复制到剪贴板!'); }).catch(err => { console.error('使用navigator.clipboard复制失败:', err); fallbackCopy(text); }); } catch (e) { console.error('复制到剪贴板出错:', e); fallbackCopy(text); } } // 备用复制方法 function fallbackCopy(text) { const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { const successful = document.execCommand('copy'); if (successful) { alert('链接已复制到剪贴板!'); } else { alert('复制失败,请手动复制。'); } } catch (err) { console.error('备用复制方法失败:', err); alert('复制失败,请手动复制。'); } document.body.removeChild(textarea); } // 初始化 function init() { // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', createExtractorButton); } else { createExtractorButton(); } // 监听URL变化 let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; // 移除旧按钮 const oldButton = document.querySelector('.yt-extractor-btn'); if (oldButton) { oldButton.remove(); } // 添加新按钮 setTimeout(createExtractorButton, 1000); } }).observe(document, { subtree: true, childList: true }); } // 启动脚本 init(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址