Last active
March 13, 2025 09:22
-
-
Save n0mimono/7743e0112fb975975704f0beb0a9fab8 to your computer and use it in GitHub Desktop.
YouTubeLiveChat with Chromium
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
const puppeteer = require("puppeteer"); | |
const VIDEO_ID = "VIDEO_ID"; | |
const YOUTUBE_LIVE_URL = `https://www.youtube.com/watch?v=${VIDEO_ID}`; | |
const INTERVAL_MS = 2000; | |
(async () => { | |
console.log("π’ Launching browser..."); | |
const browser = await puppeteer.launch({ | |
headless: true, | |
args: ["--disable-features=site-per-process", "--no-sandbox", "--disable-setuid-sandbox"], | |
}); | |
console.log(`π’ Accessing YouTube Live page... ${YOUTUBE_LIVE_URL}`); | |
const page = await browser.newPage(); | |
await page.setUserAgent( | |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" | |
) | |
await page.goto(YOUTUBE_LIVE_URL, { waitUntil: "networkidle2" }); | |
console.log("π’ Getting ytInitialPlayerResponse..."); | |
const html = await page.content(); | |
const match = html.match(/ytInitialPlayerResponse\s*=\s*(\{.+?\});/); | |
if (!match) { | |
console.error("β ytInitialPlayerResponse not found"); | |
await browser.close(); | |
return; | |
} | |
const status = JSON.parse(match[1]).playabilityStatus.status | |
console.log(`π’ Status: ${status}`); | |
console.log("π’ Searching for frame..."); | |
await page.waitForSelector("iframe#chatframe", { timeout: 10000 }); | |
const frame = await page.$("iframe#chatframe"); | |
if (!frame) { | |
console.error("β iframe#chatframe not found"); | |
await browser.close(); | |
return; | |
} | |
console.log("π’ Getting contentFrame..."); | |
const contentFrame = await frame.contentFrame(); | |
if (!contentFrame) { | |
console.error("β iframe contentFrame not found"); | |
await browser.close(); | |
return; | |
} | |
console.log("π’ Waiting for items..."); | |
await contentFrame.waitForSelector("#items", { timeout: 10000 }); | |
// scrape chat messages | |
async function scrape() { | |
const messages = await contentFrame.evaluate(() => { | |
const items = document.querySelector("#items"); | |
if (!items) { | |
return []; | |
} | |
let chatMessages = []; | |
const contents = items.querySelectorAll("yt-live-chat-text-message-renderer"); | |
for (let content of contents) { | |
const id = content.id; | |
const timestamp = content.querySelector("#timestamp")?.innerText.trim() || ''; | |
const author = content.querySelector("#author-name")?.innerText.trim() || ''; | |
const message = content.querySelector("#message")?.innerText.trim() || ''; | |
chatMessages.push({ id, timestamp, author, message }); | |
} | |
return chatMessages; | |
}); | |
console.clear(); | |
console.log(`π’ Latest chat (${new Date().toLocaleTimeString()}):`); | |
console.table(messages); | |
} | |
setInterval(scrape, INTERVAL_MS); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment