从零构建Tampermonkey脚本:实现网易云音乐免费歌曲下载

从零构建Tampermonkey脚本:实现网易云音乐免费歌曲下载 1. 认识Tampermonkey与网易云音乐下载需求第一次接触Tampermonkey俗称油猴插件是在五年前当时为了批量下载某论坛的附件资源。这个浏览器扩展的神奇之处在于它能让我们用JavaScript代码改造任何网页。今天我们就用它来解决一个实际需求在网易云音乐网页版下载免费歌曲。你可能遇到过这种情况听到一首喜欢的歌想下载却发现需要开通会员。其实平台上有大量免费歌曲是可以合法下载的只是官方没有提供直接下载按钮。通过分析网页结构我发现这些免费歌曲的音频文件其实就藏在网页代码里就像藏在迷宫中的宝藏只需要正确的工具和路径就能获取。与QQ音乐不同网易云音乐的DOM结构更加动态化。歌曲信息不会直接显示在audio标签里而是通过异步加载。这就好比你去餐厅点餐QQ音乐是直接把菜端上桌而网易云音乐需要服务员JavaScript去后厨现做。我们需要找到正确的点餐方式才能获取到音频文件。2. 环境准备与基础分析2.1 安装Tampermonkey插件在Chrome商店搜索Tampermonkey安装即可这里有个小技巧建议安装Beta版本它对ES6语法支持更好。安装后浏览器右上角会出现猴子图标点击添加新脚本就会打开编辑器界面。我习惯先写好基础框架// UserScript // name 网易云音乐免费下载 // namespace http://tampermonkey.net/ // version 0.1 // description 下载网易云音乐免费歌曲 // author 你的名字 // match https://music.163.com/* // grant GM_download // run-at document-end // /UserScript特别注意match参数要包含网易云音乐的所有子页面grant声明我们要使用的API权限。2.2 分析网易云音乐页面结构打开网易云音乐网页版播放任意免费歌曲按F12打开开发者工具。关键点来了切换到Elements面板搜索audio标签。你会发现它没有src属性——这是因为音频地址是动态加载的在Network面板筛选media类型请求播放歌曲时会看到.mp3的请求这就是我们要的音频地址观察发现歌曲名藏在title标签和meta标签里但更可靠的是从.tit类名的元素获取经过多次测试我发现音频URL的规律总是包含.mp3后缀并且域名是music.126.net。这个发现很关键它让我们可以绕过复杂的DOM操作直接定位资源。3. 核心代码实现3.1 动态监听音频地址由于网易云音乐采用动态加载我们需要用MutationObserver监听DOM变化。这就像在餐厅安装监控每当服务员端出新菜音频元素我们就能立即发现function setupObserver() { const observer new MutationObserver((mutations) { mutations.forEach((mutation) { if (mutation.addedNodes.length) { const audioNodes document.querySelectorAll(audio); audioNodes.forEach((audio) { if (audio.src.includes(.mp3) !audio.dataset.processed) { audio.dataset.processed true; setupDownloadButton(audio.src); } }); } }); }); observer.observe(document.body, { childList: true, subtree: true }); }这段代码做了三件事创建观察者监视body内所有DOM变化当发现新增节点时检查是否有audio标签确认是MP3资源且未被处理过时触发按钮创建3.2 创建下载按钮接下来我们改造播放控件区域添加下载按钮。网易云的播放器控件都在.m-playbar元素内找到合适的位置插入按钮很重要function setupDownloadButton(audioUrl) { const songName document.querySelector(.f-thide).innerText; const controlBar document.querySelector(.btns); if (!controlBar || document.getElementById(downloadBtn)) return; const downloadBtn document.createElement(a); downloadBtn.id downloadBtn; downloadBtn.className btn; downloadBtn.innerHTML i classicn-dl/i下载; downloadBtn.style.marginLeft 15px; downloadBtn.style.cursor pointer; downloadBtn.addEventListener(click, () { downloadFile(audioUrl, ${songName}.mp3); }); controlBar.appendChild(downloadBtn); }这里有几个实用技巧使用平台原有的CSS类名如btn保持样式统一添加图标使按钮更专业限制重复创建按钮的判断逻辑3.3 实现下载功能下载功能使用Tampermonkey提供的GM_download API但需要处理几个常见问题function downloadFile(url, filename) { // 处理网易云音乐的反盗链 const finalUrl url.replace(/^http:/, https:) ?x-oss-processaudio; GM_download({ url: finalUrl, name: filename, saveAs: true, onerror: (error) { console.error(下载失败:, error); alert(下载失败: ${error.error}); }, onload: () { console.log(下载完成); } }); }特别注意强制使用HTTPS避免混合内容问题添加OSS处理参数绕过部分限制完善的错误处理机制4. 高级优化与调试技巧4.1 处理动态加载延迟网易云音乐有时会延迟加载音频资源我们需要多重保障// 初始检查 setTimeout(() { const audio document.querySelector(audio); if (audio audio.src) setupDownloadButton(audio.src); }, 2000); // 启动观察者 setupObserver(); // 监听路由变化 window.addEventListener(hashchange, () { setTimeout(checkForAudio, 1000); });这种三层检测机制确保页面加载后立即检查动态变化实时监控单页面应用路由切换时重新检查4.2 样式隔离与冲突解决为防止CSS冲突建议给自定义元素添加独特前缀downloadBtn.style.cssText margin-left: 15px; cursor: pointer; background-color: #f00 !important; border-radius: 4px !important; padding: 0 12px !important; ;使用!important覆盖平台默认样式但要注意适度使用。4.3 跨域问题解决方案如果遇到跨域问题可以尝试以下方法const finalUrl new URL(url); finalUrl.searchParams.set(_t, Date.now());通过添加时间戳参数有时能绕过简单的缓存限制。5. 完整代码与使用说明将所有代码组合起来最终的完整脚本如下// UserScript // name 网易云音乐免费下载增强版 // namespace http://tampermonkey.net/ // version 1.2 // description 支持网易云音乐网页版免费歌曲下载 // author 技术爱好者 // match https://music.163.com/* // grant GM_download // grant GM_notification // run-at document-end // /UserScript (function() { use strict; // 下载功能实现 function downloadFile(url, filename) { const finalUrl new URL(url); finalUrl.protocol https:; finalUrl.searchParams.set(_t, Date.now()); GM_download({ url: finalUrl.href, name: cleanFilename(filename), saveAs: true, onerror: (error) { GM_notification({ title: 下载失败, text: error.error, timeout: 3000 }); } }); } // 清理非法文件名字符 function cleanFilename(name) { return name.replace(/[\\/:*?|]/g, ) .mp3; } // 创建下载按钮 function createDownloadButton(url) { const songName document.querySelector(.f-thide)?.innerText || 未知歌曲; const controlBar document.querySelector(.btns); if (!controlBar || document.getElementById(myDownloadBtn)) return; const btn document.createElement(a); btn.id myDownloadBtn; btn.className btn; btn.innerHTML i classicn-dl/i下载; btn.style.cssText margin-left: 15px; cursor: pointer; background-color: #f00 !important; border-radius: 4px !important; padding: 0 12px !important; ; btn.addEventListener(click, () downloadFile(url, songName)); controlBar.appendChild(btn); } // 检查音频元素 function checkForAudio() { const audio document.querySelector(audio); if (audio?.src?.includes(.mp3)) { createDownloadButton(audio.src); } } // 启动观察者 function setupObserver() { const observer new MutationObserver(() { checkForAudio(); }); observer.observe(document.body, { childList: true, subtree: true }); } // 初始化 setTimeout(checkForAudio, 1000); setupObserver(); window.addEventListener(hashchange, () { setTimeout(checkForAudio, 800); }); })();使用说明安装脚本后刷新网易云音乐页面播放任意免费歌曲等待1-2秒播放控件区域会出现红色下载按钮点击按钮即可保存MP3文件常见问题处理如果按钮不出现尝试刷新页面下载失败时检查控制台错误信息某些特殊歌曲可能受版权保护无法下载6. 法律与道德注意事项在开发和使用这类脚本时必须明确以下几点仅适用于平台明确标注为免费的歌曲不得破解或下载VIP专享内容下载的资源仅限个人使用禁止传播尊重音乐人的劳动成果支持正版技术本身是中性的关键在于如何使用。我个人的原则是用技术解决问题但绝不侵犯他人合法权益。在实际使用中这个脚本帮我保存了很多网络条件不好时想听的歌曲也希望它能帮到你。