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

您所在的位置:网站首页 track单曲 在网易云音乐网页版上加下载按键进行下载歌曲

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

#在网易云音乐网页版上加下载按键进行下载歌曲| 来源: 网络整理| 查看: 265

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

源由

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

原理

其实原理很简单,先打开 Chrome 的开发者工具的 Network 中,数据类型选为 Media,再打开网易云音乐的界面,点击一首歌,即可看到数据的 URL 地址,下载即可。 network 但这里的缺点是,重复操作,而且下载也没有歌曲名,你需要一个一个的下载,一个一个的更改歌名,所以接下来我打算解决两个点:

1. 点击下载。 2. 自动复制歌曲名。 代码解决 思路

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

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

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

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

将该条目的具体信息关掉,也就是点击 Headers 左边的小叉叉,即可看到: 发送 Ajax 的文件 发送该 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 core.js 代码片段 当看到 type: “json” 这个字段时,我们大胆的猜测 v5A.bl5q() 即为发送 Ajax 的函数。

Ajax 请求函数

我们再一次的全局搜索 v5A.bl5q 的定义代码,发送有一处代码为: Ajax 请求的代码 在该代码中我们发现:

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 对象: 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 = ` .ztz-easyd {position:relative; top:70%; font-size:15px; left:40px; color:black;} ` var html = `下载` 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 = ` .ztz-down {position:relative; top:75%; width:200px; left:-30px;} ` 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 标签,并复制了歌名。

具体代码点击代码。

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

操作界面

总结

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



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3