网易云音乐JS逆向

网易云音乐JS逆向网易云音乐之 JS 逆向 1 文章目录 2 页面分析当我们用 chrome 浏览器 推荐用谷歌 进入网易云官网 找到一首你喜欢的歌 打开 f12 功能 点击 XHR 过滤 这个时候 我们点击播放 在右侧就会重新捕获到新的网络请求 其中就包括我们需要的歌曲文件链接 就像这样 v1 csrf 这个网址就是刚刷的 在响应中可以看到 有个 url 你复制打开 就可以直接播放 我们点击一下 headers 看看怎么发送的 请求了 requesturl 用 post 发送 下面有 2 个参数表单 p



手把手教你网易云音乐JS逆向

1. 目标数据

  • 1.搜索音乐,获取音乐id
  • 2.获取url,下载音乐

2. 文章目录

目录

3. 页面分析

import requests url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=' headers = { 
    'origin': 'https://music.163.com', 'referer': 'https://music.163.com/', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36' } data = { 
    'params': '+i3P/nsviot5Ms6bDX+wUmyKAFRm7u3J7pdECwtbeVV10k41za7DjYIXSgCbNngYOO7GH+ADMz/pULLM6bItfAGGfjatL9xirPAWnwzGbr9uTul31ITnC9es8lu01n5eyP8svVHDCecGH57SU0u+hA==', 'encSecKey': '21c157c5cf0cecee8b518ee8726bba93e2dcb6e280d4d0e4c6ceea3107e5eaab6ad55ca53c713cd9893d5cf11491dae043bebccacc1c95680fff17ba34ccdfc3e3c4863eca69b671ef510a44e3f68d6fcd6e55b6d0fa320bfc82aa6a2adb641d60e7f480d5809f9c7e1963f884c9c5cf80cd81e5e08' } res = requests.post(url, headers=headers, data=data) print(res.json()) 

在这里插入图片描述
但是问题来了,params和encSeckey是啥玩意儿?这么长,一看就知道是加过密的,接下来要开启解密模式了
点击面板右上角的全局搜索,搜索encSeckey
在这里插入图片描述
有3个js文件,一个一个去找吧
在这里插入图片描述
先别着急找,像下面这样,结合起来,在第一个js文件中找到的可能性大一些,既然这样,那就开始找吧
在这里插入图片描述
小插曲:上面为什要搜encSeckey,而不搜params呢?因为用params命名的使用范围要比encSeckey广,所以用encSeckey搜好一些(经验)
















点击第一个js,进去后点击格式化,搜索encSeckey,发现有3处,在这里插入图片描述
滑到第二处,为什么是第二处呢?很简单,因为params和encSeckey在一起嘛,不在这你说在哪?好了,到这里,另外的两个js可以忽略了。
在这里插入图片描述
接下来就要分析参数了,我们往上嫖,发现两个参数分别是由bWv7o的encText、encSecKey两个属性。






接下来才是真正的js逆向了,敲黑板了!!!

既然params和encKeckey是由bWv7o产生的,那么我们就要分析bWv7o是什么了。不难发现bWv7o是由window.asrsea(JSON.stringify(i1x), bsK8C(["流泪", "强"]), bsK8C(XR1x.md), bsK8C(["爱心", "女孩", "惊恐", "大笑"]));产生的,那么接下来就找找window.asrsea是啥玩意儿

在同一作用域内的就这么点,就一个d函数

!function() { 
    function a(a) { 
    var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0", c = ""; for (d = 0; a > d; d += 1) e = Math.random() * b.length, e = Math.floor(e), c += b.charAt(e); return c } function b(a, b) { 
    var c = CryptoJS.enc.Utf8.parse(b) , d = CryptoJS.enc.Utf8.parse("00708") , e = CryptoJS.enc.Utf8.parse(a) , f = CryptoJS.AES.encrypt(e, c, { 
    iv: d, mode: CryptoJS.mode.CBC }); return f.toString() } function c(a, b, c) { 
    var d, e; return setMaxDigits(131), d = new RSAKeyPair(b,"",c), e = encryptedString(d, a) } function d(d, e, f, g) { 
    var h = { 
   } , i = a(16); return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h } function e(a, b, d,, e) { 
    var f = { 
   }; return f.encText = c(a + e, b, d), f } window.asrsea = d, window.ecnonasr = e }(); 

在这里插入图片描述
在这里插入图片描述
再点一首歌,发现e,f,g三个参数是固定的
在这里插入图片描述
综上,d参数是变化的,而且变化的地方是id,分析的时候就当它是固定的,接着往后走








原来是AES加密,熟悉AES的,肯定敲开心,不熟悉也没关系,很容易
在这里插入图片描述

function b(a, b) { 
    // urf-8编码 var c = CryptoJS.enc.Utf8.parse(b) , d = CryptoJS.enc.Utf8.parse("00708") // 固定值 , e = CryptoJS.enc.Utf8.parse(a) // AES加密,e加密文本,c秘钥,d偏移量,CBC模式 , f = CryptoJS.AES.encrypt(e, c, { 
    iv: d, mode: CryptoJS.mode.CBC }); return f.toString() } 
 function c(a, b, c) { 
    // a,b,c是固定值 var d, e; return setMaxDigits(131), // 跳进去,调试... 创建长度为132的数组 d = new RSAKeyPair(b,"",c), //  e = encryptedString(d, a) } 

由此可知,encSeckey是经过RSA加密的,但是这里的a,b,c三个参数是固定值,因此这个参数也可以用固定值

好了,到这里js逆向分析就结束了

4. 模拟加密,获取参数

class Encrypt: def __init__(self, text): self.data = { 
    'encSecKey': '01ec48cbaa77f993a988cc1f5bcd75f49eddc581f2fe2aaf564b2d4b1312cf6e0bbaddce5a4c81b38b89abd100b0f1865d22d2a8e5dd8be208eb5d6eb2f71309a165daeffe95355e1e44edd65bdf28088fe4f5e835a7d9f7569fc2530f9d17c00b51cfafbe421ebea3' } self.text = text self.key = '0CoJUm6Qyw8W8jud' def get_form_data(self): """生成表单参数""" # 随机秘钥参数,可以用固定值 i = "4JknCzx6uEXUwxpU" # 两次加密 first_encrypt = self.AES_encpyt(self.text, self.key) self.data['params'] = self.AES_encpyt(first_encrypt, i) return self.data def AES_encpyt(self, text, key): """AES加密""" # AES加密明文必须为16的整数倍 padding = 16 - len(text.encode()) % 16 text += padding * chr(padding) aes = AES.new(key.encode(), AES.MODE_CBC, b'00708') enctext = aes.encrypt(text.encode()) return b64encode(enctext).decode('utf-8') 

5.获取url,下载音乐

class NeteaseCloudMusic: def __init__(self, song): self.url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=' reqstr = ''' authority: music.163.com method: POST path: /weapi/song/enhance/player/url/v1?csrf_token= scheme: https accept: */* accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9 cache-control: no-cache content-length: 434 content-type: application/x-www-form-urlencoded cookie: __root_domain_v=.163.com; _qddaz=QD.28yaab.3jymc6.kf0ihnaf; _ntes_nnid=e7c5f90265b4d5a5bcb511efebf7a890,95; _ntes_nuid=e7c5f90265b4d5a5bcb511efebf7a890; _iuqxldmzr_=32; WM_TID=OlHvFOuIVclAQFQUAEJvJZyLuh3MwtGb; NMTID=00ODCot1Uq8CvcXIUIMmKBlPfRiyfoAAAF3NHwibw; WM_NI=%2BWiHzgkFWg%2BON3YYI0rQzlpsOW8x4BPGt%2FWRNpkD3r2Utv8U1gx6RZgvmmJQ0IpSBgdk1GvY9uIQW6BfIN7lVoHo8z1BIoa%2FdLUgKwpx6twUKJtgDlexKOu7LqWGuYApZzg%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eeb9c844a3b1aba3b24489eb8eb6d15b929a9baaaa5cace70087b64e8ab18299d02af0fea7c3b92ae989a7a9f96da99a9988aa458eed97bacc3cb28fb68df3798d89f899b74a9499bcd0d65a8eb0a5a5b27af28bbc97bb5ff3b9b8d7d152a5aaa38ec95bf497c0b4c16da8b5ffa8f553fbab87b2d63e82ba87afb66896b18890bb72f39e8790e425a8949b88ca7db4a8fa95f65f8996bc88c768a7a885b0f83d90af99a8f85383b0969be637e2a3; hb_MA-9F44-2FC2BD04228F_source=www.baidu.com; JSESSIONID-WYYY=bERBG86BVbD29X%5C35acjg8ndIoGYPEZvQ8fc0t7WUnMu3KTujvG1zqfSMIG%2By4%2FZRz9hC%2FwBN0Mf%2B%2B1RJBK2TeR96X7l%2BmS%2FHhuuqBwl7yxwe4jQ%5ChzFoFgKylb3ZdOnw6%2FqsqaUYUrJ12EVVy0m66JVlQez0T5ijmgZuOsk0KcMnUe4%3A23; WEVNSM=1.0.0; WNMCID=kctjbv.55.01.0 origin: https://music.163.com pragma: no-cache referer: https://music.163.com/ sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 ''' self.headers = HeaderPrettyDict().pretty(reqstr) self.text = '{"ids":"[' + str(song['song_id']) + ']","level":"standard","encodeType":"aac","csrf_token":""}' self.name = song['song_name'] self.singer = song['singer'] def music(self): """获取音乐的url""" data = Encrypt(self.text).get_form_data() res = requests.post(self.url, headers=self.headers, data=data) song_url = res.json()['data'][0]['url'] self.save(self.download(song_url)) def download(self, url): """下载音乐""" headers = { 
   'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'} res = requests.get(url, headers=headers) return res.content def save(self, content): """保存音乐""" # 当前文件目录 path = os.path.dirname(__file__) # 检查'data'目录是否存在,不存在则创建目录 if not os.path.exists(path+'\\data'): os.mkdir(path+'\\data') # 音乐保存路径 music_path = path+'\\data'+f'\\{self.name} {self.singer}.m4a' # 保存 if not os.path.exists(music_path): with open(music_path, 'wb') as f: f.write(content) 

6. 歌曲搜索

以上只能实现单曲下载,下面将实现歌曲搜索功能

数字 类型
1 单曲
10 专辑
100 歌手
1014 视频
1006 歌词
1000 歌单
1009 声音主播
1002 用户

开心吧!!!但是事与愿违,虽然参数一样,但是用前面的生成参数却无法get到数据,别慌,方法还是一样,断点调试

经过调试之后,我们发现就是d参数不同(d={"hlpretag":"","hlposttag":"","s":"冬眠","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}),因此只要构造d参数就能请求到数据了

在这里插入图片描述
直接上代码吧

class SearchMusic: def __init__(self, text): self.url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token=' reqstr = ''' authority: music.163.com method: POST path: /weapi/song/enhance/player/url/v1?csrf_token= scheme: https accept: */* accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9 cache-control: no-cache content-length: 434 content-type: application/x-www-form-urlencoded cookie: __root_domain_v=.163.com; _qddaz=QD.28yaab.3jymc6.kf0ihnaf; _ntes_nnid=e7c5f90265b4d5a5bcb511efebf7a890,95; _ntes_nuid=e7c5f90265b4d5a5bcb511efebf7a890; _iuqxldmzr_=32; WM_TID=OlHvFOuIVclAQFQUAEJvJZyLuh3MwtGb; NMTID=00ODCot1Uq8CvcXIUIMmKBlPfRiyfoAAAF3NHwibw; WM_NI=%2BWiHzgkFWg%2BON3YYI0rQzlpsOW8x4BPGt%2FWRNpkD3r2Utv8U1gx6RZgvmmJQ0IpSBgdk1GvY9uIQW6BfIN7lVoHo8z1BIoa%2FdLUgKwpx6twUKJtgDlexKOu7LqWGuYApZzg%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eeb9c844a3b1aba3b24489eb8eb6d15b929a9baaaa5cace70087b64e8ab18299d02af0fea7c3b92ae989a7a9f96da99a9988aa458eed97bacc3cb28fb68df3798d89f899b74a9499bcd0d65a8eb0a5a5b27af28bbc97bb5ff3b9b8d7d152a5aaa38ec95bf497c0b4c16da8b5ffa8f553fbab87b2d63e82ba87afb66896b18890bb72f39e8790e425a8949b88ca7db4a8fa95f65f8996bc88c768a7a885b0f83d90af99a8f85383b0969be637e2a3; hb_MA-9F44-2FC2BD04228F_source=www.baidu.com; JSESSIONID-WYYY=bERBG86BVbD29X%5C35acjg8ndIoGYPEZvQ8fc0t7WUnMu3KTujvG1zqfSMIG%2By4%2FZRz9hC%2FwBN0Mf%2B%2B1RJBK2TeR96X7l%2BmS%2FHhuuqBwl7yxwe4jQ%5ChzFoFgKylb3ZdOnw6%2FqsqaUYUrJ12EVVy0m66JVlQez0T5ijmgZuOsk0KcMnUe4%3A23; WEVNSM=1.0.0; WNMCID=kctjbv.55.01.0 origin: https://music.163.com pragma: no-cache referer: https://music.163.com/ sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 ''' self.headers = HeaderPrettyDict().pretty(reqstr) self.text = text def search(self): """搜索音乐,返回音乐列表""" data = Encrypt(self.text).get_form_data() res = requests.post(self.url, headers=self.headers, data=data) songlist = [] songs = res.json()['result']['songs'] for song in songs: item = { 
   } # id、歌名、歌手、封面 item['song_id'] = song['id'] item['song_name'] = song['name'] item['singer'] = song['ar'][0]['name'] # item['song_pic_url'] = song['al']['picUrl'] songlist.append(item) return songlist 

完美收工~~~

下一篇 打造网易云图形界面

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/225744.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 上午8:38
下一篇 2026年3月17日 上午8:38


相关推荐

  • dsu on tree简介及例题

    dsu on tree简介及例题dsu nbsp on nbsp treedsu on treedsu nbsp on nbsp tree 树上启发式合并 多用于对子树的暴力询问 通过使用轻重链定义来进行优化 将算法复杂度降到 O nlogn O nlogn O nlogn 算是一种优雅的暴力先用一道 dsuontree 比较模版的题来引一下 codeforces60 题意 一棵树 n 个点 每个点有一个颜色要求每个结点子树的出现哪个颜色次数最多如果有多个颜色次数同时最多 结果为这些颜色编号相加首先可以考虑暴力的写

    2026年3月17日
    1
  • Grep命令详解-9个经典使用场景

    Grep命令详解-9个经典使用场景Grep 全称 GlobalRegula 表示全局正则表达式是一个强大的文本搜索工具 采用正则匹配

    2026年3月26日
    3
  • 手把手教你搭建自己的AI智能体,超详细的用Coze搭建Agent教程

    手把手教你搭建自己的AI智能体,超详细的用Coze搭建Agent教程

    2026年3月12日
    2
  • 金融大数据风控模型「建议收藏」

    金融大数据风控模型「建议收藏」https://www.secrss.com/articles/27699导读:金融是AI赋能传统行业的重要赛道。本次分享的主要内容为金融大数据风控模型在度小满金融的实践。主要介绍金融大数据风控模型的主要技术方法与在应用层面的主要问题,并结合新冠疫情背景下,探讨下风控模型的发展。01金融大数据风控模型的技术方法1.风险管理中的金融科技风险管理中的金融科技主要包括两个方面:一方面是传统金融的风险管理,包括: A卡(ApplicationScorecard,申请评分卡) .

    2022年6月9日
    56
  • 常见电平转换电路设计参考

    常见电平转换电路设计参考一 概述 在硬件设计中有时候经常会遇到 主芯片引脚使用的 1 8V 3 3V 5V 等 连接外部接口芯片使用的 3 3V 5V 等 由于电平不匹配就必须进行电平转换 两个设备如果供电电压不一样 比如一个是 3 3V 另一个是 5V 那么在电平不匹配的情况下工作 会造成信号传输出错 如果二者电压相差较大 严重的可能会损坏芯片 二 设计 1 晶体管构成的电平转换方法

    2026年3月26日
    2
  • Windows 桌面字体背景颜色取消 

    Windows 桌面字体背景颜色取消 

    2021年7月29日
    79

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号