• 源由
  • 原理
  • 代码解决
    • 思路
    • Ajax 请求函数
    • 获取 id 和歌名
    • 点击下载
    • 利用 a 标签
    • 利用 audio 标签
    • 更改歌曲名
      • a 标签的 download 属性
      • 利用 Ajax 请求歌曲内容
      • 利用后台
      • 设置下载前询问
    • 利用剪贴板
  • 代码逻辑
  • 操作流程
  • 总结

源由

每一次放假回家的时候,都会帮家里面的人下载歌曲,当然差不多用的都是网易云音乐网页版,但是只有客户端才提供下载按键,所以每次都是从 Chrome 的开发者工具的 Network 中找链接,进行下载。所以我就想着能不能用 JS 代码来实现一个按键,一键就可以下载音乐。其实我可以直接用客户端来进行下载的,但随便为了学习一下,用 JS 来实现。

原理

其实原理很简单,先打开 Chrome 的开发者工具的 Network 中,数据类型选为 Media,再打开网易云音乐的界面,点击一首歌,即可看到数据的 URL 地址,下载即可。

但这里的缺点是,重复操作,而且下载也没有歌曲名,你需要一个一个的下载,一个一个的更改歌名,所以接下来我打算解决两个点:

1.  点击下载。
2.  自动复制歌曲名。

代码解决

思路

大概看云音乐界面情况,可知播放控制栏为固定的,则可以肯定,数据是通过 Ajax 发送,获取歌曲的 URL 地址,再进行加载播放。

在开发者的 Network 中记录了所有的请求数据,请求的数据是按照时间来排序的,我们按照刚才的原理可以获取到歌曲的 URL 地址,所以在该歌曲的请求数据上面,肯定会有一个发送 Ajax 的请求,来请求歌曲的 URL 地址信息。

所以我的思路是这样的,找到该 Ajax 请求函数,模拟发送歌曲信息请求,获取歌曲的 URL 地址,然后进行下载。

我们把 Network 中的数据类型选中为 All,即可看到发送的 Ajax 歌曲信息请求。

在 Network 的 Name 栏可看到 55ea198fb 开头(最后一个)的请求,是发送的歌曲请求,所以在上面会有一个歌曲信息的请求,我们边往上找边看请求的返回数据,大概知道该歌曲信息请求的返回值中肯定有歌曲的 URL 地址。在往上的第四条我们发现了该请求。

将该条目的具体信息关掉,也就是点击 Headers 左边的小叉叉,即可看到:

发送该 Ajax 请求的是云音乐的 core.js 文件。

将 core.js 文件下载下来,再格式化代码,为什么我会把该代码下载下来呢,我是这么想的他发送 Ajax 请求,可能会是先填入发送的相关 URL 地址,然后需要的时候再加上参数调用发送,所以我们还需要知道发送该 Ajax 的具体 URL 地址是什么,在 Network 中点击发送获取歌曲信息的条目,可看到为:

Request URL: http://music.163.com/weapi/song/enhance/player/url?csrf_token=

将该 URL 在格式化后的 core.js 代码中进行查找。我们首先查找 weapi/song/enhance/player/url 无果,再次去除一些,再查找 song/enhance/player/url

当看到 type: “json” 这个字段时,我们大胆的猜测 v5A.bl5q() 即为发送 Ajax 的函数。

Ajax 请求函数

我们再一次的全局搜索 v5A.bl5q 的定义代码,发送有一处代码为:

在该代码中我们发现:

j5o["csrf_token"] = v5A.gC7v("__csrf");
Y5d = Y5d.replace("api", "weapi");

我们很肯定这个就是云音乐发送 Ajax 的代码,只不过被混淆了,不过我们可以进行黑箱操作,我们把该代码块提取出来,也就是 (function() { … })(); 中的代码,我们把提取出来的代码,复制在游览器云音乐页面的开发者工具 Console 中,但报错了:

点击最右的超链接,发现原来代码在 if (v5A.bl5q.redefine) return; 返回了,这里应该是为了避免重复定义 Ajax 接口设置的,在这里不管,我们直接把他去掉,即可运行成功了,此时我们就可以在 Console 中,调用 v5A.bl5q() 发送 Ajax 请求了。

v5A.bl5q("/api/song/enhance/player/url", {type: "json",query: {ids: JSON.stringify([this.cP6J.id]),br: DEFAULT_BR},onload: this.bGl1x.f5k(this),onerror: this.bGl1x.f5k(this)})

可以在 Console 中输入这个,即可获得歌曲的 URL 地址,但这里要发送有两个参数,一个是 ids、br。ids 应该是歌曲的 id 进行加密,所以this.cP6J.id 为歌曲的 id。全局找到 DEFAULT_BR,可知道这个是一个变量,我猜测这里应该是音质的选择:

var DEFAULT_BR = 128e3;


嗯,对的,就是音质的选择,那我们可以将该 DEFAULT_BR = 320e3 获取极高的音质。

onload 请求成功时调用,onerror 请求失败是调用,我们可以用下面这个函数来打印响应的数据。

var a = function() {console.log(arguments)} 

ids: JSON.stringify([id]),id 为歌曲的 id,点击一首歌的主页可看见 URL 为 http://music.163.com/#/song?id=418602116, 即 id 为418602116,br 为320e3,将该请求代码复制到 Console 中,可将 onerror 函数去掉。

即可看到,该请求成功,在返回的数据中,已经得到了该 id 对应歌曲的 URL 地址。

此时我们已经知道获取歌曲 URL 地址的 Ajax 函数。

若参数还无法确定时,可采用在线调试云音乐的 core.js 代码,利用我之前写的在线调试 JS 的方法(点击即可访问),获取 Ajax 发送的请求数据,即可以在代码中输出对应的参数信息,即可观察到。

   b4f.bFx0x = function() {this.zJ3x();console.log("************debug***********请求数据")console.log(this.cP6J.id)console.log(DEFAULT_BR)console.log("************debug***********请求数据")v5A.bl5q("/api/song/enhance/player/url", {type: "json",query: {ids: JSON.stringify([this.cP6J.id]),br: DEFAULT_BR},onload: this.bGl1x.f5k(this),onerror: this.bGl1x.f5k(this)})};

获取 id 和歌名

在偶然间我发现,云音乐有一个 window.player 对象:

这里面有一个 getPlaying() 函数,里面记录了当前播放音乐的信息:

所以我们可以通过这个来获取到 id 和歌名

var track = window.player.getPlaying().track
// 歌曲 id
var id = track.id
// 歌手名
var singer = track.artists.map(e => e.name).join('/');
// 歌曲名
var song = track.name;var name = `${singer} - ${song}.mp3`;

点击下载

我们通过上面,已经得到发送 Ajax 的函数和需要的参数,所以我们可以在页面中插入一个固定位置的超链接,点击即可下载。

// 添加 css
var css = `<style>.ztz-easyd {position:relative; top:70%; font-size:15px; left:40px; color:black;}</style>`
var html = `<a class="ztz-easyd" hidefocus="true"onclick="downloadCurrent(event)">下载</a>`document.head.insertAdjacentHTML('beforeEnd', css)
document.body.insertAdjacentHTML('beforeEnd', html);

然后再 downloadCurrent() 加入下载的逻辑,先获取到歌曲 id,再发送 Ajax 请求歌曲信息,然后得到的歌曲 URL 地址,再用 window.open(mp3Url) 打开。

经过尝试后,发现一个问题,Chrome 游览器会拦截这个弹窗,这样不太友好。

利用 a 标签

window.open(mp3Url) 替换为设置该下载也就是 a 标签的 href 属性值的后,可弹出。

有个问题,点击第一次时,并没有弹出,因为点击时,href 属性没有值,href 的值是点击函数中赋值的,点击第二次时,可访问但是访问的是前一次的歌曲 URL,这次的歌曲 URL 还未赋值给 href。这是一个问题,但我们也可以解决的。

当点开 a 标签时,新的页面是一个 audio 标签播放界面。所以我就想,直接在云音乐页面中插入 audio 标签,并且是可视的,这样一点击就可以下载了,就可以避免上面的 href 赋值问题了。

利用 audio 标签

所以该下载的 a 标签,改名为加载,意思就是加载 audio 标签。

云音乐的播放过程是这样的,请求下来的歌曲 URL,放入 audio 标签中,即可播放,而在 Chrome 游览器中,默认的 audio 的控制栏中有下载按键。

// css 属性
var css = `<style>.ztz-down {position:relative; top:75%; width:200px; left:-30px;}</style>`
document.head.insertAdjacentHTML('beforeEnd', css)// 停止页面的播放
window.player.pause();var audio = new Audio();
audio.src = mp3Url;
audio.controls = true;
audio.setAttribute('class', 'ztz-down');
audio.removeAttribute('preload');
document.body.appendChild(audio);

更改歌曲名

a 标签的 download 属性

a 标签中有一个 download 属性,可以支持设置下载名,但仅限于同源的,而他这个歌曲 URL 是不同源的。

利用 Ajax 请求歌曲内容

我又想着,再通过 Ajax 请求歌曲 URL,这样就能把返回的歌曲内容转化为 base64,再加到 a 标签的 src 中,这样就不受同源策略影响了,download 属性就可以用了。

但尝试了无解,Ajax 禁止非同源加载数据,而且请求歌曲 URL,不能使用 CORS(跨域资源共享 )。

这里也发现了一个问题,数据时请求下来的,受游览器的同源安全策略,数据只是没有在游览器中显示。

利用后台

其实这里用后台来解决的话,很方便,弄一个支持 CORS 的后台,请求的时候把歌曲的 URL 地址和歌曲的名字(FILENAME)发过去,然后返回数据,在响应头中设置 MIME,这样下载的时候,就会自带名字。

Content-Disposition: attachment; filename=FILENAME

设置下载前询问

我们也可以在下载的时候,更改名字,Chrome 默认的是,一点击就下载,这里我们就无法进行更改名字了,所以应该把 Chrome 下载前询问打开,方便我们进行修改名字。

打开 Chrome 设置 -> 高级 -> 下载内容 -> 打开下载前询问每个文件的保存位置 

具体操作。

利用剪贴板

我们更改文件名是采用设置下载前询问,

到目前为止我们已经解决了点击下载,就剩下 自动复制歌曲名了

每次上 github 下载项目时,都会有一个点击即可复制下载链接,我通过搜索大概知道了原理,利用的是剪贴板实现的功能,用户点击时,自动把内容复制。

我们采用clipboard 这个库,具体使用方法点击,大概如下:

1.  引用该 JS 文件。
2.  设置可自动复制的 class,运行 new ClipboardJS('.btn'); btn 为class。
3.  设置需要点击复制的超链接或者按键的 class、data-clipboard-text 属性,前者就是第二步设置的可自动复制的 class,后者就是复制的内容。

代码逻辑

1.  云音乐页面中插入 a 标签,并有点击事件。
2.  点击后,暂停播放,生成 audio 标签,并复制了歌名。

具体代码点击代码。

操作流程

  1. 点击 复制里面的全部 JS 代码。
  2. 复制到游览器云音乐界面的开发者工具的 Console 中,回车运行。关闭开发者工具。
  3. 打开 Chrome 下载文件前询问,设置下载前询问。
  4. 播放需要下载的歌,然后点击加载,再点击 audio 的下载图标,粘贴(Ctrl+C)更改名字后下载即可。

总结

多尝试,多观察,就能知道哪些能做,哪些不能做。

在网易云音乐网页版上加下载按键进行下载歌曲相关推荐

  1. vue2仿网易云音乐网页版

    项目地址:https://github.com/Huang-xaofan/Heart-pleasant-music/tree/shared 第一步:需要开启本地服务器 cmd 进入 网易云音乐本地接口 ...

  2. 从网易云音乐网页版无登陆下载MP3的办法

    用chrome浏览器就能办到 现在,你还为下载不到满意的音乐而苦恼吗?

  3. 抓取网易云音乐网页歌单(url)js

    var importJs=document.createElement('script') //在页面新建一个script标签 importJs.setAttribute("type&quo ...

  4. 网易云音乐Android版使用的开源组件

    转自:http://www.jianshu.com/p/f31ab96a32f3 网易云音乐Android版从第一版使用到现在,全新的 Material Design 界面,更加清新.简洁.同样也是音 ...

  5. 音乐无界限,听见好时光—网易云音乐Linux版震撼来袭!

    为了带来更好的音乐体验,实现对音乐高品质的追求,经过网易云音乐与深度科技团队长达半年多的联合开发,大家期待已久的网易云音乐正式登陆 Linux 平台! 网易云音乐是一款专注于发现与分享的音乐产品,依托 ...

  6. [转]网易云音乐Android版使用的开源组件

    原文链接 网易云音乐Android版从第一版使用到现在,全新的 Material Design 界面,更加清新.简洁.同样也是音乐播放器开发者,我们确实需要思考,相同的功能,会如何选择.感谢开源,让我 ...

  7. 网易云音乐刷听歌量_网易云音乐极速版悄然上线!听歌体验同之前没有差别

    了解更多热门资讯.玩机技巧.数码评测.科普深扒,点击右上角关注我们 ---------------------------------- 7月2日消息,"网易云音乐极速版"App在 ...

  8. 鸿蒙系统怎么安装网易云音乐,网易云音乐鸿蒙版 - 魔法系统之家下载

    网易云音乐鸿蒙版是一款非常好用听音乐软件,丰富的音乐资源库,多种听歌音质选择,各种热门人气新出歌曲统统一网打尽,在网易云音乐鸿蒙版中各种音乐类型都可以看,还有私人fm.每日推荐扥各种特色功能,下面一起 ...

  9. 网易云音乐电脑版怎么下载电台节目 主播电台节目下载教程

    网易云音乐不仅可以听歌,还可以在主播电台中,听到各类主播的声音,下面我们就来讲讲网易云音乐电脑版怎么下载电台节目,一起来看教程吧! 网易云音乐电脑版怎么下载电台节目 主播电台节目下载教程 网易云音乐P ...

最新文章

  1. 解决网通英文wiki无法显示图片问题【20100723更新】
  2. KeyError: [] not found in axis_高调又有质感,女星最爱的至IN单品原来是它!
  3. Codeforces Round #715 (Div. 1) B. Almost Sorted 找规律
  4. 计算机知识竞赛主持人开场词,护理竞赛主持人台词:主持人开场白台词
  5. pytorch查看当前学习率
  6. 程序员入门--两年养成之路
  7. 【GPORF源码编译】GDB源码编译之gprof交叉编译
  8. 修改oracle安装目录权限设置权限,oracle rac环境,安装目录权限被修改乱了,怎么恢复?...
  9. 2021财经直播系统源码 H5直播聊天室 金融财经 喊单系统 股票外汇直播间
  10. 数据库系统概论第五版(第 1 章 绪论)习题答案
  11. matlab求导程序,「matlab求导」matlab 求导的一个简单程序 - 金橙教程网
  12. 京瓷打印机驱动安装教程,怎么安装方便点?
  13. 短网址生成-nodejs实现
  14. uni.showToast(OBJECT)消息提示框
  15. java使用递归,复制文件夹下的所有子文件
  16. 酷派7296刷机包 高仿IOS美化 细节精仿
  17. 微信公众号客服系统怎么生成能追踪效果的二维码?
  18. 手机QQ浏览器如何支持html5,手机QQ浏览器五大策略打造HTML5平台
  19. Activity生命周期和启动模式
  20. 最形象的卷积神经网络详解:从算法思想到编程实现(转载)

热门文章

  1. 数学回味系列之19 - 有趣的数字
  2. 南柯服务器压力,性能/负载/压力测试 - Mr.南柯 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
  3. [cesium] 卫星雷达传感器,雷达探测效果
  4. 【备战春招/秋招系列】美团Java面经总结终结篇 (附详解答案) 1
  5. 使用HTML5和Less框架3的自适应Web设计
  6. pikachu~~~CSRF(get,post,token)
  7. NeRF与GAN的交融(一)——NeRF论文阅读
  8. 斜杠‘/‘ 和反斜杠‘\‘
  9. Cocos Creator之打包设置横竖屏
  10. 计算机主机通电启动不了,电脑不通电,开机没反应?这里有问题!