如何用Node.js获取YouTube Music歌曲页面HTML及元数据?
解决Node.js获取YouTube Music歌曲页面HTML及元数据的问题
一、修复Axios请求的Header配置
YouTube Music的反爬机制会校验多个请求头,仅设置User-Agent不足以绕过拦截,补充完整的浏览器请求头可以解决基础拦截问题:
const axios = require("axios"); async function fetchYTMPage(songUrl) { try { const res = await axios.get(songUrl, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'en-US,en;q=0.5', 'Referer': 'https://music.youtube.com/', 'DNT': '1', 'Upgrade-Insecure-Requests': '1' }, decompress: true // 启用gzip压缩,匹配浏览器行为 }); return res.data; } catch (err) { console.error('请求失败:', err.message); return null; } } // 使用示例 fetchYTMPage("https://music.youtube.com/watch?v=SONG_ID").then(html => { if (html) { // 执行你已有的元数据提取逻辑 console.log('成功获取页面HTML'); } });
二、使用无头浏览器(Puppeteer)获取渲染后的页面
如果静态请求仍然失败,说明YouTube Music启用了JS渲染或更严格的反爬策略,用Puppeteer模拟真实浏览器加载是最可靠的方案:
- 安装依赖:
npm install puppeteer
- 示例代码:
const puppeteer = require("puppeteer"); async function fetchYTMPageWithPuppeteer(songUrl) { const browser = await puppeteer.launch({ headless: 'new', // 新版无头模式,更接近真实浏览器行为 args: ['--no-sandbox', '--disable-setuid-sandbox'] // 解决Linux环境权限问题 }); const page = await browser.newPage(); // 配置浏览器参数 await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'); await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.5', 'Referer': 'https://music.youtube.com/' }); try { // 等待页面完全加载(包括JS渲染内容) await page.goto(songUrl, { waitUntil: 'networkidle2' }); const html = await page.content(); return html; } catch (err) { console.error('加载页面失败:', err.message); return null; } finally { await browser.close(); } } // 使用示例 fetchYTMPageWithPuppeteer("https://music.youtube.com/watch?v=SONG_ID").then(html => { if (html) { // 执行你的元数据提取逻辑 console.log('成功获取渲染后的页面HTML'); } });
三、替代方案:使用维护更好的第三方库或官方API
如果不想自己处理HTML解析,可以尝试以下更高效的方案:
1. 第三方库改进版
youtube-music-v2相比旧版youtube-music-api维护更活跃,支持按ID查询和处理非官方艺人:
安装:
npm install youtube-music-v2
示例代码:
const { YouTubeMusic } = require("youtube-music-v2"); async function getSongMetadata(songId) { const ytMusic = new YouTubeMusic(); try { const song = await ytMusic.getSong(songId); return { artist: song.artists.map(a => a.name).join(', '), album: song.album?.name || '无专辑', year: song.album?.year || '未知', coverUrl: song.thumbnails.at(-1)?.url // 取最高分辨率封面 }; } catch (err) { console.error('获取元数据失败:', err.message); return null; } } // 使用示例 getSongMetadata("SONG_ID").then(metadata => { if (metadata) { console.log('歌曲元数据:', metadata); } });
2. YouTube Data API(官方方案)
如果需要稳定服务,可使用官方YouTube Data API v3,通过歌曲ID获取详细信息(需API密钥,有免费配额):
安装依赖:
npm install googleapis
示例代码:
const { google } = require('googleapis'); const youtube = google.youtube({ version: 'v3', auth: 'YOUR_API_KEY' // 替换为你的API密钥 }); async function getSongMetadataViaAPI(songId) { try { const response = await youtube.videos.list({ part: 'snippet,contentDetails', id: songId }); const snippet = response.data.items[0].snippet; return { artist: snippet.channelTitle, album: snippet.description.match(/Album: (.+)/)?.[1] || '无专辑', year: snippet.publishedAt.split('-')[0], coverUrl: snippet.thumbnails.high.url }; } catch (err) { console.error('API请求失败:', err.message); return null; } } // 使用示例 getSongMetadataViaAPI("SONG_ID").then(metadata => { if (metadata) { console.log('歌曲元数据:', metadata); } });
内容的提问来源于stack exchange,提问作者Batimius
相关产品推荐
相关产品推荐

