**1、**首先我们打开歌单内的任意一首歌曲,在该页面下打开chrome的开发者工具后切换到Network后重新刷新页面,找到请求到该歌曲播放源的URL,如下图:

找到后切换到Headers,可以看到其为一个post请求,URL为:‘https://music.163.com/weapi/song/enhance/player/url?csrf_token=’ ,从上图可知这个URL返回的是json,里面包含的是歌曲的信息及其MP3播放链接,请求时携带的参数为params及encSecKey,为了找到该歌曲播放源的URL,我们需解密params及encSecKey。

**2、**以encSecKey为关键词进行全局搜索,找到加密该参数的文件,如下图:

接着找到该参数是如何生成的,从下图可以看到其与bYd2x有关,为了得到该encSecKey我们需知道bYd2x这个值是如何生成的,所以我们按下图的操作设置断点,然后重新刷新页面进行调试。

按上图操作后,我们可以看到bYd2x这个函数中有四个参数,其中i8a与我们的歌曲id有关,另外三个参数我们也需要找出来,通过多次调试后可以看到其返回的值其实都是固定值,调试结果如下图:



通过上面分析得知其需要的参数值后,我们需找到window.asrsea这个函数是如何执行的,这样才能从其返回值里得到encText(即params)和encSecKey。在调试模式下我们按下图将鼠标悬浮到这个函数上可知它其实就是d函数,因此我们需找到d函数的定义。

**3、**按上图将鼠标悬浮到这个函数上后我们点击悬浮框上的蓝色字体可以到达定义d函数的位置,如下图所示:

从上图可知d函数与a函数,b函数及c函数有关,且excText的值由b函数加密了两次,因此我们要分别找到这些函数的定义并写成python语言,同样的我们在var h={}这行打上断点后重新刷新页面。首先我们找到a函数,其定义如下图:

可以看到它其实是在一堆字符串中随机找出16个字符串,将其写成python为:

get_i=execjs.compile(r"""function a(a) {var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";for (d = 0; a > d; d += 1)e = Math.random() * b.length,e = Math.floor(e),c += b.charAt(e);return c}
""")
i=get_i.call('a',16)

接着调试到b函数,其定义如下图:

从上图b 函数中的f可知其采用了 AES 加密,加密模式 为CBC ,加密的密文是 e 【其实就是我们上面说的i8a】,c是密钥【其实就是buU9L([“爱心”, “女孩”, “惊恐”, “大笑”])的返回值,是一个常量】,偏移量 d是一个常量为 ‘0102030405060708’,将其写成python语言为:

def to_16(key):while len(key) % 16 != 0:key += '\0'return str.encode(key)
def AES_encrypt(text, key, iv):#text为密文,key为公钥,iv为偏移量bs = AES.block_sizepad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)encryptor = AES.new(to_16(key), AES.MODE_CBC,to_16(iv))encrypt_aes = encryptor.encrypt(str.encode(pad2(text)))encrypt_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')return encrypt_text

而excText的值(即params)是由b函数加密了两次的,传入的参数为i8a(即密文)及【buU9L([“爱心”, “女孩”, “惊恐”, “大笑”])】的返回值(即公钥),因此定义得到params的函数:

    g='0CoJUm6Qyw8W8jud'def get_params(id,g):#id为歌曲idiv="0102030405060708"i=get_i.call('a',16)encText=str({'ids': "[" + str(id) + "]", 'br': 128000, 'csrf_token': ""})#i8areturn AES_encrypt(AES_encrypt(encText,g, iv), i, iv)

最后调试到c函数,其定义如下图:

由上图知c 函数是采用 RSA 加密,传入参数a是a函数生成的16位字符串,b 为【buU9L([“流泪”, “强”])的值】, c 为【buU9L(Rg4k.md)的值】,将其写成python语言为:

def RSA_encrypt(text, pubKey, modulus):text=text[::-1]rs=int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)return format(rs, 'x').zfill(256)

而encSecKey的值就是c函数的返回值,因此定义得到encSecKey的函数为:

i=get_i.call('a',16)
b="010001"
c='00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
def get_encSecKey(i, b, c):return RSA_encrypt(i, b, c)

至此,我们就全部解析完d函数,并且也得到了请求’https://music.163.com/weapi/song/enhance/player/url?csrf_token=’ 这个URL时携带的参数为params及encSecKey,而这个post请求返回的json数据里包含了歌曲的信息及其MP3播放源链接,有了播放源链接我们就可以用urllib.request.retrieve这个请求方式直接将远程数据下载到本地。

**4、**综上分析可知我们只需有歌曲的id就能下载了,为此我们要提取所创建的歌单中所有歌曲的id。此处以歌单:https://music.163.com/playlist?id=2624438246 为例,点开该歌单后打开chrome的开发者工具,刷新后找到下面这个请求,看到这个请求的Response中并没有正常地显示数据,因此可知歌单内容是通过js动态渲染出来的。

对于这种情况,我们一般也会想到2种解决方案,使用selenium或者继续破解js,但还有一个更好的解决方法,就是使用requests-html这个库,之所以选这个库时因为其内置了pyppeteer这个库支持js渲染,且相对与selenium来说高效许多,我们通过下面这个代码先来看下请求后返回的页面源代码,然后再用xpath编写提取规则就能得到每首歌的id及歌名。

到此所有问题都以解决,最后奉上全部源码及运行结果:

import urllib.request,os,json
from lxml import etree
from requests_html import HTMLSession
import execjs,requests,random
import base64,codecs
from Crypto.Cipher import AESdef to_16(key):while len(key) % 16 != 0:key += '\0'return str.encode(key)def AES_encrypt(text, key, iv):bs = AES.block_sizepad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)encryptor = AES.new(to_16(key), AES.MODE_CBC,to_16(iv))encrypt_aes = encryptor.encrypt(str.encode(pad2(text)))encrypt_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')return encrypt_textdef RSA_encrypt(text, pubKey, modulus):text=text[::-1]rs=int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)return format(rs, 'x').zfill(256)def set_user_agent():USER_AGENTS = ["Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)","Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)","Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)","Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)","Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6","Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1","Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0","Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5"]user_agent = random.choice(USER_AGENTS)return user_agent#获取i值的函数,即随机生成长度为16的字符串
get_i=execjs.compile(r"""function a(a) {var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";for (d = 0; a > d; d += 1)e = Math.random() * b.length,e = Math.floor(e),c += b.charAt(e);return c}
""")class WanYiYun():def __init__(self):self.playlist_url='https://music.163.com/playlist?id=2624438246'#歌单地址self.song_url='https://music.163.com/weapi/song/enhance/player/url?csrf_token='self.g = '0CoJUm6Qyw8W8jud'#buU9L(["爱心", "女孩", "惊恐", "大笑"])的值self.b = "010001"#buU9L(["流泪", "强"])的值# buU9L(Rg4k.md)的值self.c = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'self.i=get_i.call('a',16)#随机生成长度为16的字符串self.iv = "0102030405060708"  # 偏移量if not os.path.exists("d:/music"):os.mkdir('d:/music')self.headers={  'User-Agent':set_user_agent(),'Referer':'https://music.163.com/','Content-Type':'application/x-www-form-urlencoded'}#由于歌单内容是通过JS生成的,所以此处运用requests_html这个库来实现JS渲染从而获得歌单def get_music_list(self):session = HTMLSession()response = session.get(self.playlist_url)html = etree.HTML(response.content.decode())song_list = html.xpath("//ul[@class='f-hide']/li/a")music_list = []for song in song_list:music_id = song.xpath('.//@href')music_name = song.xpath('.//text()')music_list.append({'id': music_id, 'name': music_name})return music_listdef get_params(self,id):encText=str({'ids': "[" + str(id) + "]", 'br': 128000, 'csrf_token': ""})return AES_encrypt(AES_encrypt(encText,self.g, self.iv), self.i, self.iv)def get_encSecKey(self):return RSA_encrypt(self.i, self.b, self.c)def download(self):music_list=self.get_music_list()for music in music_list:music_id=music['id'][0].split('=')[1]music_name=music['name'][0]formdata={'params':self.get_params(music_id),'encSecKey':self.get_encSecKey()}response=requests.post(self.song_url, headers=self.headers, data=formdata)download_url=json.loads(response.content)["data"][0]["url"]if download_url:try:# 根据音乐url地址,用urllib.request.retrieve直接将远程数据下载到本地urllib.request.urlretrieve(download_url, 'd:/music/' + music_name+ '.mp3')print('Successfully Download:'+music_name+ '.mp3')except:print('Download wrong~')
if __name__ == '__main__':wanyiyun=WanYiYun()wanyiyun.download()


100行不到就能下载到你想要的歌曲,完美!!!

python实战:破解网易云音乐params及encSecKey加密参数实现歌单内任意歌曲下载(附参数分析过程)相关推荐

  1. 网易云音乐params和encSecKey参数生成代码

    网易云音乐params和encSecKey参数生成代码 不废话,直接上code import requests import json import base64 import random impo ...

  2. 项目总结3 类似网易云音乐导航栏指示器(个性推荐、歌单等)的简单实现(一)

    我们先来看看网易云音乐导航栏指示器是什么样的. 箭头指向的蓝色框就是导航指示器,点击之后下面的view会跟着移动,每个button下面还有个小红线跟着.这个效果其实不难实现,我们先来分析分析. 在iO ...

  3. 细数网易云音乐上那些值得收藏的民谣歌单。

    单独开了一个专栏,希望与大家一起分享音乐.在此之前抓取了网易云音乐的曲库,接下来将对曲库中的歌曲按照类别进行整理并且推荐给大家. 已整理分享歌单如下(点击前往): 日语歌单 放松歌单 ACG歌单 轻音 ...

  4. 细数网易云音乐上那些值得收藏的粤语歌单。

    单独开了一个专栏,希望与大家一起分享音乐.在此之前抓取了网易云音乐的曲库,接下来将对曲库中的歌曲按照类别进行整理并且推荐给大家. 已整理分享歌单如下(点击前往): 日语歌单 放松歌单 ACG歌单 轻音 ...

  5. 网易云音乐params和encSecKey生成原理

    1.首先来个整体的解析 params:经过两次AES加密后的数据(128位,CBC,pkcs5padding) 第一加密:要加密的是真正要发送的明文数据(数据的具体格式,下面会讲),密钥固定为0CoJ ...

  6. JS逆向 2021-8-16 网易云音乐 params、encSecKey参数

    声明:文章仅供学习,禁止非法用途,侵权请告知删除 文章目录 前言 一.网页分析 二.参数获取 1.参数定位 2.参数解密 三.部分代码 结尾 前言 地址:aHR0cHM6Ly9tdXNpYy4xNjM ...

  7. Python爬取网易云音乐歌手歌曲和歌单(爬虫)

    Python爬取网易云音乐歌手歌曲和歌单 是 仅供学习参考 Python爬取网易云音乐网易云音乐歌手歌曲和歌单,并下载到本地 ①找到要下载歌手歌曲的链接,这里用的是: https://music.16 ...

  8. python网络爬虫网易云音乐_手把手教你写网络爬虫(1):网易云音乐歌单

    大家好,<手把手教你写网络爬虫>连载开始了!在笔者的职业生涯中,几乎没有发现像网络爬虫这样的编程实践,可以同时吸引程序员和门外汉的注意.本文由浅入深的把爬虫技术和盘托出,为初学者提供一种轻 ...

  9. python爬取网易云音乐飙升榜音乐_python爬取网易云音乐热歌榜 python爬取网易云音乐热歌榜实例代码...

    想了解python爬取网易云音乐热歌榜实例代码的相关内容吗,FXL在本文为您仔细讲解python爬取网易云音乐热歌榜的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:python,网易热歌榜 ...

最新文章

  1. 使用docker搭建Hadoop
  2. 运维人,你应该了解的三张武功心法图(转载)
  3. 排序之选择排序:简单选择+堆排序
  4. 剪映专业版PC端清理缓存与日志
  5. oracle解析md5,Oracle中的MD5加密详解
  6. Java编程思想—第三四五六七章
  7. Python中代码书写规范与基本使用
  8. 2020年天猫双11官宣:分两波购买 比往年多3天
  9. android intent包装,Android 中的 Intent
  10. 在Visual Studio 2012中使用VMSDK开发领域特定语言(二)
  11. Vue3.x 推荐使用 mitt.js
  12. lvm硬盘管理及LVM扩容
  13. 大学excel题库含答案_大学excel操作测试题「附答案」
  14. redis配置文件下载
  15. Servlet和Servlet容器概念
  16. java实行图片上写字
  17. pdf打不开无法启动计算机丢失,电脑打开PDF文档时发生错误文件已损坏怎么修复...
  18. 【目标跟踪 MOT】JDE - Towards Real-Time Multi-Object Tracking
  19. 使用 html 写一个表达爱意的网页
  20. 苹果侧边滑动返回_iPhone X侧边按键使用教程,满满都是黑科技啊

热门文章

  1. DLT645协议解析(一)---初识 DLT645协议
  2. Linux screenshot
  3. 易语言从html中获取验证码,易语言过腾讯滑块验证码模块源码和调用例程
  4. 3、Xendesktop更改发布桌面的显示名称(MCS静态桌面)
  5. 几款数据恢复工具的使用
  6. 【网络安全管理员三级鉴定题】一
  7. 父母该如何协助孩子安全上网?
  8. 《三国武神赵云传》隐私政策
  9. 估值超400亿美元的京东物流,已成顺丰最有力的竞争对手
  10. 有哪些真实的兵棋推演模拟训练仿真系统软件解决方案