不想看分析的,可以直接跳到最下面看总结,我给出了整个Api接口。
随便找首歌,然后打开chrome的开发助手,看看调用了哪些请求。
先猜测是用异步的方式加载的,所以先看看开发助手中Network→XHR
这个选项卡里面的内容。
既然如此,那就不必祭出我的Fiddler大法了。现在看看这个请求需要哪些参数。
请求体:
params:WEmLVbEilCTe1z6i2ZoKWNwjZIam6x2GcmvZrq6lfRPqN02pvltAqFZIjgWcvbjAl2//qGYZ9lcxvfao+gXZIzrbtpj0zXUFAo3ZAOdJuSpnvNtZMBgkM7n3QPBMXCqkFO0fflLkcBmz14OR70oGZXrVea8r2pj5HEjH4d9aK0mv6i6Gntq5Vfc/O5dHiohMmiixMtslkak3lk6FnGCSrrlNUt+vIgZ1YeVRu1Bm/XE=
encSecKey:8e9ad6a76467073d73452d27497af2de382c20ec40d9fa40079ae46c4555050a2e1805abeb554f5f72982f0694182be821f8114a17304be0d1fd4d537b3caea0766a771d967932b3050cff0e5ea64f26dd6f1fbb55cddcda9c0d6f08955e596a269b0f56a2b18a10325ccb896005a1302e5fd2a607f93ec77febaf50168b00d4
看到请求体的时候,就发现肯定是加密了的。现在先不管,先去postman模拟一下请求,看看需要哪些东西才能够获取到评论。
可以看到,只需要设置了请求体,就能正确的获取到返回结果了。现在的关键就是,找到请求体的加密逻辑了。
怎么找呢?不知道。反正就一个个js文件看呗,搜关键词params
或encSecKey
,看看有哪些js文件中出现了这两个关键字。
此处花费时间:n小时…
而且可以看到,两个关键字还是在一起出现的,那么可以99%的确定,这个core_xxx.js
文件应该就是我们需要的了。下面简称为core.js
。
下面就是跟着js逻辑,一路向上找。
首先看到,encSecKey
和params
这两个参数的值都是由bVj7c
这个变量提供的,而bVj7c
这个变量又来自window.asrsea()
这个方法。现在可以推测,加密函数应该就是这个window.asrsea()
了。将这个js文件保存到本地,用一个IDEA系列的编辑器打开这个js文件,方便我们阅读源码。
在core.js
中搜索关键字asrsea
,发现在这里将函数d
赋值给了window.asrsea()
。所以加密方法就是d
了。
后面我尝试了继续向下分析,发现调用的函数越来越多,大概有多少呢?我数了下我删减后的core.js
,涉及到的方法数量大概在40多个,代码行数在700行左右,关键还是混淆过的。
到这里,非js大牛的话基本可以放弃这个思路了。
现在换个思路,我只是想要加密后的参数而已,没必要去弄懂它的内部逻辑。而且,我现在知道了加密函数的入口在哪,那么我只要知道加密函数需要哪些参数就好了。然后我再通过调用它的加密函数,将我的参数进行加密即可。
思路有了,现在关键是如何知道加密函数需要的参数都是什么。如果我们能让它把函数的参数打印到控制台就好了。
回想之前找js文件的时候,发现这个core.js
是独立存在的,意味着这个js文件是通过网络请求后单独返回的,也就意味着我们可以拦截请求,返回一个我们修改后的core.js
。
在加密函数的开头,添加上打印代码:
...
function d(d, e, f, g) {
console.group("加密函数");
console.log("参数d:"+d);
console.log("参数e:"+e);
console.log("参数f:"+f);
console.log("参数g:"+g);
console.groupEnd();
var h = {}, i = a(16);
return h.encText = b(d, g),
h.encText = b(h.encText, i),
h.encSecKey = c(i, e, f),
h
}
...
现在,祭出Fiddler大法,拦截core.js
文件的请求,返回我们修改后的core.js
。
Ok,现在回到chrome,按下ctrl+f5,强制刷新一次页面。然后看看控制台是否打印出了参数信息。
完美!加密函数的参数信息已经拿到了。可以看到,4个参数中,只有第一个参数是一直在变化的,另外3个参数都是不变的。
现在就是找到获取评论的时候,第一个参数传入的值是什么了,根据筛选发现,就是这个了:
现在,去chrome的控制台,试试调用加密函数,传入上面的参数,获取到加密后的参请求参数。
OK,加密后的参数已经拿到了,现在去postman试试看看,我们的逻辑是不是对的。
根据测试发现,我们的逻辑没有错。现在分析看看,这个接口是如何判断获取的是哪首歌的评论。
首先发现的是在url中,R_SO_4_28854182
,最后的那串数字,发现和歌曲页面的url中一样:https://music.163.com/#/song?id=28854182。可以知道,最后那串数字应该就是歌曲id。这时想到前面调用加密函数的时候,传入的第一个参数中似乎也有涉及到歌曲id,因此大体上推断出,网易云的后端应该就是通过这两者进行判断是获取哪首歌的评论数据的。
按道理,到这里其实整个获取歌曲评论的接口已经分析完了。后面如果要调用这个接口,关键就是调用core.js
中的加密函数。这个的话,python和java都提供了方法,我这里就不详细阐述了。
现在我来说说一个大坑。你试试不改变请求参数,即params
和encSecKey
,只改变请求url中的歌曲id,再去请求一次看看。发现了吗?居然正确返回了!!!这意味着什么?这意味着,其实整个接口根本不需要每次都去调用加密函数,只要请求体是经过加密函数加密的就可以了。也就是说,请求体可以随便复用,只需要调用一次加密函数,后面需要获取不同的歌曲的评论的时候,只需要改变url中的歌曲id就好了。呵呵,网易云牛逼!
请求体中的参数params
和encSecKey
固定值,获取不同的歌曲只需要在url最后拼接歌曲id即可。
请求url:https://music.163.com/weapi/v1/resource/comments/R_SO_4_歌曲id
请求方式:Post
请求头:Content-Type: application/x-www-form-urlencoded
请求体:
params:mX9cplDJw86EnpHkBIvZE3UcyfqU5gEtefZGKFg1Fw4nZSwiWCYnrRwio95A1BnaZFV84rDC7ko4Gb8g+ky5tkEduK0v5cLXtySjKHly/xe0mgNhxwES9nErCSlNPZ2nNdXLJIya6Emi85YOI5J6rxFXPNjLxIjLpDYLRVwkI6usmT7NiNSCQHa4aBdwB1tX46hhkkXTEN1MfWYqa+5E9VWOTT4eDcDLfJat/x1zdhk=
encSecKey:8cc2704ada0806649f737ac789d91401909f9132fb20c00cb2237f7a42447c048cd27389a2e24d912eaad52278f4bf1bec059fb9387a4af563283e66e0cdb530183f7cab66cc494c752c10b96e840d7e3cc1262c983d172b1a9ad4e7a4cb22c1eb7f66547dc9340bb5b241822b515461e0953f48cebd2ca1c8139bef61f3f722