最近有一部很火的青春励志言情剧《亲爱的,热爱的》,其主人公之一佟年是一个网络歌手,其粉丝足有百万。好奇的我一搜,网易云音乐真有这么一号人物,真是现实和电视剧傻傻分不清楚。于是我就想着爬一下网易云音乐,分析一波这真假粉丝,(因为我觉得这个粉丝数目肯定存在刷粉丝的嫌疑~)。
虽然通过观察分析发现,只能爬取前50页的粉丝也就是1000个最新的粉丝,用于数据分析必然是没有成效的,但还是记录一下破解网易云音乐反爬的过程。
打开粉丝的页面,发现其是一个POST请求,包含params和encSecKey两个表单数据,而这个数据显然是经过了加密,我们需要进入js来一看究竟。
在js中搜索encSecKey发现这两个表单数据应该就在下图所示的函数中。
var bXY4c = window.asrsea(JSON.stringify(i3x), bqu5z(["流泪", "强"]), bqu5z(QE7x.md), bqu5z(["爱心", "女孩", "惊恐", "大笑"]));
params和encSecKey都是由window.asrea这个函数生成的,其一共包含四个参数,其中第二个参数中QE7x.md是固定的,和第二个第四个加密方式相同。(这wyy前端究竟是有什么样的故事,“流泪”,“女孩”,“惊恐”,“大笑”,莫名诡异)
QE7x.md = ["色", "流感", "这边", "弱", "嘴唇", "亲", "开心", "呲牙", "憨笑", "猫", "皱眉", "幽灵", "蛋糕", "发怒", "大哭", "兔子", "星星", "钟情", "牵手", "公鸡", "爱意", "禁止", "狗", "亲亲", "叉", "礼物", "晕", "呆", "生病", "钻石", "拜", "怒", "示爱", "汗", "小鸡", "痛苦", "撇嘴", "惶恐", "口罩", "吐舌", "心碎", "生气", "可爱", "鬼脸", "跳舞", "男孩", "奸笑", "猪", "圈", "便便", "外星", "圣诞"]
我们先定义这四个参数分别为para1,para2,para3,para4。
再来看看window.asrsea是什么,window.asrsea=d,而d函数如下图所示
函数d传入四个参数,加密后返回,使用了另外三个函数a、b、c,它们又分别如下图所示,这里abcd参数和函数名称混淆,但我们只要抓住其本质,一个一个对应分析,就可以得出正确结果。
a函数生成16个随机字符,b函数是AES加密,模式为CBC,给定了初始向量'0102030405060708',c函数是RSA加密。
# 生成16个随机字符,即a函数 def _generate_random_strs(length): string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" # 控制次数参数i i = 0 # 初始化随机字符串 random_strs = "" while i < length: e = random.random() * len(string) # 向下取整 e = math.floor(e) random_strs = random_strs + list(string)[e] i = i + 1 return random_strs
# AES加密,即b函数 def _AESencrypt(msg, key): # 如果不是16的倍数则进行填充(paddiing) padding = 16 - len(msg) % 16 msg = msg + padding * chr(padding) # 用来加密或者解密的初始向量(必须是16位) iv = '0102030405060708'
cipher = AES.new(key, AES.MODE_CBC, iv) # 加密后得到的是bytes类型的数据 encryptedbytes = cipher.encrypt(msg) # 使用Base64进行编码,返回byte字符串 encodestrs = base64.b64encode(encryptedbytes) # 对byte字符串按utf-8进行解码 enctext = encodestrs.decode('utf-8') return enctext
# RSA加密,即c函数 def _RSAencrypt(randomstrs, key, f): # 随机字符串逆序排列 string = randomstrs[::-1] # 将随机字符串转换成byte类型数据 text = bytes(string, 'utf-8') seckey = int(codecs.encode(text, encoding='hex'), 16)**int(key, 16) % int(f, 16) return format(seckey, 'x').zfill(256)
d函数中的encText调用了两次b函数,也就是两次AES加密,第一次参数为para1和para4,第二次参数为第一次加密后的结果和16位随机字符。
d函数中的encSecKey调用了c函数,参数是16为随机字符和para2,para3。
知道了整个加密的过程,我们来确定一下这四个参数的值。我们利用Fiddler进行js替换,在控制台打印出我们需要的四个参数就是para1,para2,para3,para4。首先下载网页上的js文件,并加入下面的代码,接着在Fiddler中选择替换js文件。
console.info(i3x);console.info(bqu5z(["流泪", "强"]));console.info(bqu5z(QE7x.md));console.info(bqu5z(["爱心", "女孩", "惊恐", "大笑"]));var bXY4c = window.asrsea(JSON.stringify(i3x), bqu5z(["流泪", "强"]), bqu5z(QE7x.md), bqu5z(["爱心", "女孩", "惊恐", "大笑"]));
刷新网页,可以发现控制台输出了相关的参数信息。这里很重要的一点,需要将网页缓存清除,删除之前的浏览记录。
在控制台上我们可以看到这四个参数分别是什么数据,后面三个参数为固定数值,而第一个参数是由userId,offset等构造而成的。
这样我们就可以得到这两个表单数据,用requests.post就可以轻松获得我们想要的信息。