網(wǎng)易云音樂Web端的接口分析
個人博客同步更新盆繁,閱讀體驗也會更好一點旬蟋,歡迎訪問
先看看云村有哪些榜單:
飆升榜的訪問地址:
http://music.163.com/#/discover/toplist?id=19723756
各個榜單地址就是最后的 id 不一樣。但是這里的有一個坑冕碟,相當(dāng)之坑,我不說安寺,你先好好看鏈接就能發(fā)現(xiàn)了。
好的首尼,我查看一下源代碼
這樣沒有問題吧挑庶,有問題言秸,錯,要看框架源代碼迎捺。井仰。。不清楚網(wǎng)易咋搞的破加,但是如果你在源代碼里找你想要的俱恶,你會啥都找不到。喜歡用快捷鍵的范舀,直接就在這里跪了合是。
好的,在 框架里找到列表锭环,是個table聪全,先別忙著解析,在找找辅辩,你會發(fā)現(xiàn)有 json 數(shù)據(jù)
就是列表的难礼,直接就拿了。解析網(wǎng)頁的工具那么多玫锋,我還是喜歡美麗湯蛾茉。
soup = BeautifulSoup(response.text, 'lxml')
json_data = json.loads(soup.find('textarea').text)
拿到j(luò)son 數(shù)據(jù),就有子頁面了撩鹿,id 就是單曲的id 了谦炬,從json 里拿
https://music.163.com/#/song?id=472361096
好像沒啥好說的,唯一要說的地方就是节沦,要注意鏈接里面的#
號键思,如果不是什么框架的限制,那只能說是心機(jī)啊甫贯。用代碼訪問鏈接的時候一定要把#
號去掉吼鳞,否則返回的一直是云村的首頁,根本拿不到數(shù)據(jù)赔桌。不管你用什么 webkit纬乍, Js 渲染啦,延時等待啦都不行墓贿。請注意!請注意队伟!請注意!
發(fā)現(xiàn)這個坑之后就可以到單曲頁看看了锈颗。
比如我們想要拿熱門評論击吱,但是很快又會發(fā)現(xiàn)又找不到想要的數(shù)據(jù)了覆醇,去掉 #
號也不行永脓,沒錯這次是真的 Js 渲染了頁面了憨奸。到這里兩條路,第一條板甘,selenium 渲染頁面盐类,再得到page_source在跳;第二條猫妙,分析接口齐帚,模擬請求數(shù)據(jù)对妄。
相對來說剪菱,用 selenium 會簡單一些琅豆,但是抓取效率慢一點茫因。這是一個保險的方案冻押。所以我們先試試第二條路。
抓包次兆,我們發(fā)現(xiàn)這么一個接口:
參數(shù)詳情
獲取評論的接口
獲取評論
https://music.163.com/weapi/v1/resource/comments/R_SO_4_472361096?csrf_token=
params=漓库。渺蒿。茂装。。
encSecKey=彼妻。训挡。为肮。肤京。棋枕。重斑。太長了不寫了
- post 請求
- 472361096 是單曲的id
- 請求的param窥浪,csrf_token漾脂,因為我沒有登陸骨稿,所以沒有值,但是也可以請求到
- 剩下就是請求體蓝牲,params 和 encSecKey例衍,你可以看一下截圖佛玄,這兩個是最難搞的梦抢,寫些什么鬼也不知道奥吩,程序員就是這么互相為難的了腮介。
- 返回的是評論列表 json
好的端衰,我們先從服務(wù)端的響應(yīng)中看一下有沒有這兩個鬼旅东,我看了抵代,沒有禀倔。你可以自己看一下救湖,萬一你找到了,就可以跳過了邑闺。
響應(yīng)里面沒有陡舅,那就是生成的靶衍,js生成的,我們找一下js涛酗。有很多亂七八糟的js燕刻,最后我在core.js里面找到這么一句:
好的卵洗,對上號了籍滴,基本就確定是生成的了孽惰。什么?怎么找的勋功?搜索關(guān)鍵字啊坦报,兩萬多行經(jīng)過混淆的代碼,難道一行一行讀翱裥片择!
可以看到,我們要的兩個鬼骚揍,params 和 encSecKey 都是來自 byw6q
這個對象字管,我不知道叫不叫對象啊,我 js 學(xué)的淺信不,如果說錯了,歡迎指正啊。
重點就是拿到 byw6q
,可以看到 byw6q
來自 window.asrsea 糊昙,看函數(shù)名就知道又是那些加密算法猩谊。暗甥。辜膝。忱辅。頭疼。
我們搜索 asrsea
,看到:
ok签舞,看一下 d
是啥:
好的傍药,不能再截圖了仑氛,太麻煩了出吹。
我們看到 函數(shù) d
有 4 個參數(shù)炬太,那就對上號了。最后返回 h, h 就是我們前面說的 byw6q
的對象,里面有我們要的 params 和 encSecKey 榛斯。
可以看到,函數(shù)d
里面有 函數(shù)a
,函數(shù)b
尸疆,函數(shù)c
......為什么有省略號呢嗜浮,因為 a,b抱完,c 里面有其他的各種函數(shù)、變量庙楚,大概有這么多纳账。
額官扣,怎么搞铣揉,證明你的時候到了额湘,如果你又懂js,又懂python旁舰,這是最好的表現(xiàn)機(jī)會锋华,把js的邏輯用python來寫一遍,然后執(zhí)行python里面的 函數(shù)d
箭窜,得到 params 和 encSecKey 毯焕,繼續(xù)愉快的爬蟲。具體怎么搞呢磺樱,比如說我們先看js的 函數(shù)a
:
我們可以把他翻譯成pyhton:
def a(a):
b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
c = ""
for d in range(0, a):
e = random.random() * len(b)
e = math.floor(e)
c += b[int(e)]
return c
ok 原汁原味的翻譯是這樣的纳猫,可以優(yōu)化一下:
def a(a):
b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
c = ""
for d in range(0, a):
e = random.randint(0,len(b))
c += b[e]
return c
那執(zhí)行 js 里面的 函數(shù)a
,和執(zhí)行python 里面的 函數(shù)a
結(jié)果是不是一樣的竹捉,這里當(dāng)然不是一樣啊芜辕,沒看到里面有個 random 嗎?但是块差,效果是一樣的侵续。
因為函數(shù) a 的作用,就是生成一個長度為參數(shù)a
憨闰,的隨機(jī)字符串状蜗,看上面 函數(shù)d
的截圖,參數(shù)a
是16鹉动,就是長度為16的隨機(jī)字符串轧坎。所以這里只要是16位,所有字符都在 變量b
里的字符串就行了训裆。甚至可以不隨機(jī)眶根。
所以蜀铲,先看懂,再寫属百,會節(jié)省很多的時間记劝,一五一十的翻譯,那就逗逼了族扰。
上面講了大神的做法厌丑,我的水平也就翻譯一下函數(shù)a
玩玩,剩下的 函數(shù)b
渔呵,函數(shù)c
是直接看不懂怒竿,只知道是在進(jìn)行字符串的編碼解碼,加密解密扩氢,在函數(shù)c
還用到了 RSA 算法耕驰,有興趣的可以深入的了解一下,這些東西才是真正的一勞永逸的录豺,搞懂之后朦肘,基本它只要撅屁股你就知道了。
講講我的做法:
- 最終我要的是
函數(shù)d
的返回值 - 那我就試一下直接跑js里面的
函數(shù)d
來得到双饥,方法有很多 -
函數(shù)d
一共有四個參數(shù)媒抠,所以我要做的就是獲取這些參數(shù),交給函數(shù)d
ok咏花,我們就開始獲取函數(shù)d
的四個參數(shù)趴生,使用 Fiddler 的自動響應(yīng)功能,我們提前準(zhǔn)備好一份本地的core.js,當(dāng)請求 core.js時昏翰,給他替換成本地的core.js苍匆,而本地的js跟服務(wù)端的js基本是一樣的,唯一不同是矩父,我們自己的js 锉桑,在執(zhí)行函數(shù)d
的時候會將參數(shù)打印出來。
拷貝一份core.js,修改函數(shù)d
:
就是添加上通過控制臺打印參數(shù)窍株,在 Fiddler 里面設(shè)置自動響應(yīng)規(guī)則:
可以看到我設(shè)置了兩個,為什么呢攻柠,因為如果不設(shè)置 pt_song_index.js 的話球订,控制臺會報錯,也加載不出來評論瑰钮,管他呢冒滩,那就連他一起,做一份本地的浪谴。
設(shè)置好了之后开睡,我們就到單曲頁去刷新因苹,換個單曲再試一下,得到這樣的結(jié)果:
看到?jīng)]篇恒,p1,p2,p3,p4分別就是我們要的參數(shù)啊扶檐,經(jīng)過多次的實驗,可以得出
- p2,p3,p4是不變的
- p1 是個json 字符串
- 它調(diào)用了多次胁艰,只有p1參數(shù)在變款筑,但是,只有p1 參數(shù)中有 id 才會是我們要的腾么,因為它要標(biāo)識啊
小功告成奈梳,暫時確定 四個參數(shù)如下,其中 p1 還有另外一種形式解虱,那個應(yīng)該是和翻頁有關(guān)攘须,這里就不管了,給各個參數(shù)的值:
p1={"id":"484730186","lv":-1,"tv":-1,"csrf_token":""}
p2=010001
p3=00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7
p4=0CoJUm6Qyw8W8jud
p1的id是當(dāng)前單曲的id殴泰。
有了參數(shù)阻课,我們就可以調(diào)用函數(shù)d
了,方法很多艰匙,你可以新建一個js 文件限煞,把函數(shù)d
拷進(jìn)去,因為用到函數(shù)a
员凝,函數(shù)b
署驻,函數(shù)c
,所以這些也得拷進(jìn)去健霹,以此類推用到的都扔進(jìn)去旺上。也得幾百行代碼:
執(zhí)行d,這里用我用的是 execjs糖埋,也可以用 pyv8宣吱,等等很多方式,
h = execjs.compile(js_code).call('d', '{"id":%s,"lv":-1,"tv":-1,"csrf_token":""}' % song_id,
settings.NETEASE_P2,
settings.NETEASE_P3,
settings.NETEASE_P4)
說明一下吧,反正廢話都說了這么多了瞳别,函數(shù)名:d征候,后面是對應(yīng)的p1,p2,p3,p4,四個參數(shù)祟敛,最后得到 h 疤坝,里面就有我們要的那兩個什么鬼,params 和 encSecKey馆铁。
得到params 和 encSecKey 之后跑揉,我們就用 Postman 模擬一下:
ok,這樣就得到數(shù)據(jù)了。知道這獲取那兩個什么之后历谍,一切都變得很簡單了哦现拒。
這里稍微注意一下模擬header,得有下面這兩個才行:
本到這里就差不多望侈,剩下的只是實踐印蔬。爬蟲代碼我就不給出了,但是我給一些截圖吧甜无,真想學(xué)的話扛点,你不會介意打一遍的,而且岂丘。陵究。。
解析榜單或者歌單的方法:
那個 core1.js 我是想給一下的奥帘,因為那個容易出錯铜邮,但是有700行代碼,搞個網(wǎng)盤吧
鏈接:http://pan.baidu.com/s/1i53hhWT 密碼:9g7n
獲取歌詞的接口
獲取單曲的歌詞
這個我就不詳細(xì)講了寨蹋,除了接口地址不同松蒜,好像其他都一樣。
返回已旧,有點多秸苗,上一下圖
具體的歌詞
好累啊,歡迎打賞运褪。