Created
October 6, 2025 14:00
-
-
Save minanagehsalalma/4775f0ba18758ffa2e63dfa22895e46a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // YouTube Comments Extractor - With Full Reply Support | |
| // Run this in the browser console while on a YouTube video page | |
| async function extractAllYouTubeComments() { | |
| console.clear(); | |
| console.log('%c🎬 YouTube Comments Extractor (With Replies)', 'font-size: 16px; font-weight: bold; color: #00ff00'); | |
| // First, expand ALL "Show replies" buttons | |
| console.log('📂 Expanding all reply threads...'); | |
| let expandCount = 0; | |
| let iteration = 0; | |
| while (iteration < 10) { // Try up to 10 times | |
| const replyButtons = Array.from(document.querySelectorAll('ytd-button-renderer#more-replies button, #more-replies button')) | |
| .filter(btn => btn.offsetParent !== null); // Only visible buttons | |
| if (replyButtons.length === 0) { | |
| console.log(`✓ No more reply buttons found (checked ${iteration + 1} times)`); | |
| break; | |
| } | |
| console.log(` Found ${replyButtons.length} reply buttons, clicking...`); | |
| for (const btn of replyButtons) { | |
| try { | |
| btn.click(); | |
| expandCount++; | |
| await sleep(200); | |
| } catch (e) { | |
| // Button might have disappeared, continue | |
| } | |
| } | |
| // Scroll a bit to trigger any lazy loading | |
| window.scrollBy(0, 500); | |
| await sleep(800); | |
| iteration++; | |
| } | |
| console.log(`✓ Expanded ${expandCount} reply threads`); | |
| await sleep(1000); // Wait for replies to render | |
| // Now extract everything | |
| console.log('⚙️ Extracting all comments and replies...'); | |
| const comments = []; | |
| const commentThreads = document.querySelectorAll('ytd-comment-thread-renderer'); | |
| console.log(` Found ${commentThreads.length} comment threads`); | |
| for (const thread of commentThreads) { | |
| // Main comment | |
| const mainComment = thread.querySelector('#comment #main'); | |
| if (mainComment) { | |
| const author = mainComment.querySelector('#author-text span')?.textContent.trim() || | |
| mainComment.querySelector('#author-text')?.textContent.trim() || 'Unknown'; | |
| const text = mainComment.querySelector('#content-text')?.textContent.trim() || ''; | |
| const likes = mainComment.querySelector('#vote-count-middle')?.textContent.trim() || '0'; | |
| const time = mainComment.querySelector('.published-time-text a')?.textContent.trim() || ''; | |
| if (text) { | |
| comments.push({ | |
| type: 'comment', | |
| author, | |
| text, | |
| likes, | |
| time | |
| }); | |
| } | |
| // Get all replies in this thread | |
| const replySection = thread.querySelector('#replies'); | |
| if (replySection) { | |
| const replies = replySection.querySelectorAll('ytd-comment-renderer #main'); | |
| for (const reply of replies) { | |
| const replyAuthor = reply.querySelector('#author-text span')?.textContent.trim() || | |
| reply.querySelector('#author-text')?.textContent.trim() || 'Unknown'; | |
| const replyText = reply.querySelector('#content-text')?.textContent.trim() || ''; | |
| const replyLikes = reply.querySelector('#vote-count-middle')?.textContent.trim() || '0'; | |
| const replyTime = reply.querySelector('.published-time-text a')?.textContent.trim() || ''; | |
| if (replyText) { | |
| comments.push({ | |
| type: 'reply', | |
| author: replyAuthor, | |
| text: replyText, | |
| likes: replyLikes, | |
| time: replyTime, | |
| parentAuthor: author | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (comments.length === 0) { | |
| alert('❌ No comments found! Make sure:\n1. You scrolled to the comments section\n2. Comments are enabled for this video\n3. The page is fully loaded'); | |
| return null; | |
| } | |
| // Count comments vs replies | |
| const mainComments = comments.filter(c => c.type === 'comment').length; | |
| const replies = comments.filter(c => c.type === 'reply').length; | |
| // Format for AI summary | |
| const formatted = comments.map((c, i) => { | |
| if (c.type === 'comment') { | |
| return `${c.author} (${c.likes} likes, ${c.time}):\n${c.text}`; | |
| } else { | |
| return ` ↳ REPLY by ${c.author} (${c.likes} likes, ${c.time}):\n ${c.text}`; | |
| } | |
| }).join('\n\n'); | |
| // Create downloadable file | |
| const blob = new Blob([formatted], { type: 'text/plain' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'youtube_comments_' + Date.now() + '.txt'; | |
| a.id = 'download-link-yt-comments'; | |
| console.log('\n' + '='.repeat(60)); | |
| console.log('%c✅ EXTRACTION COMPLETE!', 'font-size: 18px; font-weight: bold; color: #00ff00'); | |
| console.log('='.repeat(60)); | |
| console.log(`📊 Total extracted: ${comments.length}`); | |
| console.log(` 💬 Main comments: ${mainComments}`); | |
| console.log(` ↳ Replies: ${replies}`); | |
| console.log('\n' + '='.repeat(60) + '\n'); | |
| // Show preview with replies | |
| console.log('%c📝 PREVIEW (showing comment + replies structure):', 'font-weight: bold; font-size: 14px'); | |
| const previewItems = comments.slice(0, 5); | |
| previewItems.forEach(c => { | |
| const prefix = c.type === 'reply' ? ' ↳ ' : ''; | |
| const shortText = c.text.substring(0, 80) + (c.text.length > 80 ? '...' : ''); | |
| console.log(`${prefix}${c.author}: ${shortText}`); | |
| }); | |
| console.log('\n' + '='.repeat(60) + '\n'); | |
| // Store data globally | |
| window.ytCommentsData = formatted; | |
| window.ytCommentsRaw = comments; | |
| // Add download button to page | |
| a.style.cssText = 'position:fixed;top:10px;right:10px;z-index:99999;padding:15px 25px;background:#ff0000;color:white;text-decoration:none;border-radius:5px;font-weight:bold;font-size:16px;box-shadow:0 4px 8px rgba(0,0,0,0.3);cursor:pointer;'; | |
| a.textContent = `📥 Download ${mainComments} Comments + ${replies} Replies`; | |
| document.body.appendChild(a); | |
| // Try to copy to clipboard | |
| try { | |
| await navigator.clipboard.writeText(formatted); | |
| console.log('%c✅ All comments + replies copied to clipboard!', 'color: #00ff00; font-weight: bold; font-size: 14px'); | |
| console.log('%cPaste directly into Claude or ChatGPT now!', 'color: #00ff00'); | |
| } catch (e) { | |
| console.log('%c⚠️ Could not auto-copy. Run:', 'color: #ffaa00; font-weight: bold'); | |
| console.log(' copy(window.ytCommentsData)'); | |
| } | |
| console.log('\n' + '='.repeat(60)); | |
| console.log('%c📥 QUICK COMMANDS:', 'font-weight: bold; font-size: 14px'); | |
| console.log('copy(window.ytCommentsData) // Copy formatted text'); | |
| console.log('console.log(window.ytCommentsData) // View all text'); | |
| console.log('console.log(window.ytCommentsRaw) // View raw data'); | |
| console.log('='.repeat(60) + '\n'); | |
| alert(`✅ SUCCESS!\n\n📊 ${mainComments} comments + ${replies} replies = ${comments.length} total\n\n✓ Copied to clipboard\n✓ Red download button added\n\nReady to paste into AI!`); | |
| return { | |
| raw: comments, | |
| formatted: formatted, | |
| count: comments.length, | |
| mainComments: mainComments, | |
| replies: replies | |
| }; | |
| } | |
| function sleep(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| // Run the extraction | |
| extractAllYouTubeComments(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment