美文网首页IOS开发爬虫程序员
网易云音乐Web端的接口分析

网易云音乐Web端的接口分析

作者: wuhtt | 来源:发表于2017-06-22 15:12 被阅读10122次

    网易云音乐Web端的接口分析

    个人博客同步更新,阅读体验也会更好一点,欢迎访问

    先看看云村有哪些榜单:

    飙升榜的访问地址:

    http://music.163.com/#/discover/toplist?id=19723756
    

    各个榜单地址就是最后的 id 不一样。但是这里的有一个坑,相当之坑,我不说,你先好好看链接就能发现了。

    好的,我查看一下源代码

    这样没有问题吧,有问题,错,要看框架源代码。。。不清楚网易咋搞的,但是如果你在源代码里找你想要的,你会啥都找不到。喜欢用快捷键的,直接就在这里跪了。

    好的,在 框架里找到列表,是个table,先别忙着解析,在找找,你会发现有 json 数据

    就是列表的,直接就拿了。解析网页的工具那么多,我还是喜欢美丽汤。

    soup = BeautifulSoup(response.text, 'lxml')
    json_data = json.loads(soup.find('textarea').text)
    

    拿到json 数据,就有子页面了,id 就是单曲的id 了,从json 里拿

    https://music.163.com/#/song?id=472361096
    

    好像没啥好说的,唯一要说的地方就是,要注意链接里面的# 号,如果不是什么框架的限制,那只能说是心机啊。用代码访问链接的时候一定要把#号去掉,否则返回的一直是云村的首页,根本拿不到数据。不管你用什么 webkit, Js 渲染啦,延时等待啦都不行。请注意!请注意!请注意!

    发现这个坑之后就可以到单曲页看看了。

    比如我们想要拿热门评论,但是很快又会发现又找不到想要的数据了,去掉 # 号也不行,没错这次是真的 Js 渲染了页面了。到这里两条路,第一条,selenium 渲染页面,再得到page_source;第二条,分析接口,模拟请求数据。

    相对来说,用 selenium 会简单一些,但是抓取效率慢一点。这是一个保险的方案。所以我们先试试第二条路。

    抓包,我们发现这么一个接口:

    参数详情

    获取评论的接口

    获取评论

    https://music.163.com/weapi/v1/resource/comments/R_SO_4_472361096?csrf_token=

    params=。。。。

    encSecKey=。。。。。。太长了不写了

    1. post 请求
    2. 472361096 是单曲的id
    3. 请求的param,csrf_token,因为我没有登陆,所以没有值,但是也可以请求到
    4. 剩下就是请求体,params 和 encSecKey,你可以看一下截图,这两个是最难搞的,写些什么鬼也不知道,程序员就是这么互相为难的了。
    5. 返回的是评论列表 json

    好的,我们先从服务端的响应中看一下有没有这两个鬼,我看了,没有。你可以自己看一下,万一你找到了,就可以跳过了。

    响应里面没有,那就是生成的,js生成的,我们找一下js。有很多乱七八糟的js,最后我在core.js里面找到这么一句:

    好的,对上号了,基本就确定是生成的了。什么?怎么找的?搜索关键字啊,两万多行经过混淆的代码,难道一行一行读啊!

    可以看到,我们要的两个鬼,params 和 encSecKey 都是来自 byw6q这个对象,我不知道叫不叫对象啊,我 js 学的浅,如果说错了,欢迎指正啊。

    重点就是拿到 byw6q,可以看到 byw6q来自 window.asrsea ,看函数名就知道又是那些加密算法。。。。头疼。

    我们搜索 asrsea,看到:

    ok,看一下 d是啥:

    好的,不能再截图了,太麻烦了。

    我们看到 函数 d 有 4 个参数,那就对上号了。最后返回 h, h 就是我们前面说的 byw6q 的对象,里面有我们要的 params 和 encSecKey 。

    可以看到,函数d里面有 函数a函数b函数c......为什么有省略号呢,因为 a,b,c 里面有其他的各种函数、变量,大概有这么多。

    额,怎么搞,证明你的时候到了,如果你又懂js,又懂python,这是最好的表现机会,把js的逻辑用python来写一遍,然后执行python里面的 函数d,得到 params 和 encSecKey ,继续愉快的爬虫。具体怎么搞呢,比如说我们先看js的 函数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 原汁原味的翻译是这样的,可以优化一下:

    def a(a):
        b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        c = ""
        for d in range(0, a):
            e = random.randint(0,len(b))
            c += b[e]
        return c
    

    那执行 js 里面的 函数a ,和执行python 里面的 函数a 结果是不是一样的,这里当然不是一样啊,没看到里面有个 random 吗?但是,效果是一样的。

    因为函数 a 的作用,就是生成一个长度为参数a,的随机字符串,看上面 函数d 的截图,参数a是16,就是长度为16的随机字符串。所以这里只要是16位,所有字符都在 变量b 里的字符串就行了。甚至可以不随机。

    所以,先看懂,再写,会节省很多的时间,一五一十的翻译,那就逗逼了。

    上面讲了大神的做法,我的水平也就翻译一下函数a玩玩,剩下的 函数b函数c是直接看不懂,只知道是在进行字符串的编码解码,加密解密,在函数c还用到了 RSA 算法,有兴趣的可以深入的了解一下,这些东西才是真正的一劳永逸的,搞懂之后,基本它只要撅屁股你就知道了。

    讲讲我的做法:

    1. 最终我要的是函数d的返回值
    2. 那我就试一下直接跑js里面的函数d来得到,方法有很多
    3. 函数d一共有四个参数,所以我要做的就是获取这些参数,交给函数d

    ok,我们就开始获取函数d的四个参数,使用 Fiddler 的自动响应功能,我们提前准备好一份本地的core.js,当请求 core.js时,给他替换成本地的core.js,而本地的js跟服务端的js基本是一样的,唯一不同是,我们自己的js ,在执行函数d的时候会将参数打印出来。

    拷贝一份core.js,修改函数d:

    就是添加上通过控制台打印参数,在 Fiddler 里面设置自动响应规则:

    可以看到我设置了两个,为什么呢,因为如果不设置 pt_song_index.js 的话,控制台会报错,也加载不出来评论,管他呢,那就连他一起,做一份本地的。

    设置好了之后,我们就到单曲页去刷新,换个单曲再试一下,得到这样的结果:

    看到没,p1,p2,p3,p4分别就是我们要的参数啊,经过多次的实验,可以得出

    1. p2,p3,p4是不变的
    2. p1 是个json 字符串
    3. 它调用了多次,只有p1参数在变,但是,只有p1 参数中有 id 才会是我们要的,因为它要标识啊

    小功告成,暂时确定 四个参数如下,其中 p1 还有另外一种形式,那个应该是和翻页有关,这里就不管了,给各个参数的值:

    p1={"id":"484730186","lv":-1,"tv":-1,"csrf_token":""}

    p2=010001

    p3=00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7

    p4=0CoJUm6Qyw8W8jud

    p1的id是当前单曲的id。

    有了参数,我们就可以调用函数d了,方法很多,你可以新建一个js 文件,把函数d拷进去,因为用到函数a函数b函数c,所以这些也得拷进去,以此类推用到的都扔进去。也得几百行代码:

    执行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)
    

    说明一下吧,反正废话都说了这么多了,函数名:d,后面是对应的p1,p2,p3,p4,四个参数,最后得到 h ,里面就有我们要的那两个什么鬼,params 和 encSecKey。

    得到params 和 encSecKey 之后,我们就用 Postman 模拟一下:

    ok,这样就得到数据了。知道这获取那两个什么之后,一切都变得很简单了哦。

    这里稍微注意一下模拟header,得有下面这两个才行:

    本到这里就差不多,剩下的只是实践。爬虫代码我就不给出了,但是我给一些截图吧,真想学的话,你不会介意打一遍的,而且。。。

    解析榜单或者歌单的方法:

    那个 core1.js 我是想给一下的,因为那个容易出错,但是有700行代码,搞个网盘吧

    链接:http://pan.baidu.com/s/1i53hhWT 密码:9g7n

    获取歌词的接口

    获取单曲的歌词

    http://music.163.com/weapi/song/lyric?csrf_token=

    这个我就不详细讲了,除了接口地址不同,好像其他都一样。

    返回,有点多,上一下图

    具体的歌词

    好累啊,欢迎打赏。

    相关文章

      网友评论

      • small李:说得好,跟没说一样
      • c2ede34a4cb1:有没有获取歌曲mp3地址的api分析呀
      • 秋犯:刚试了下,音乐排行榜要加个uesr-agent不然只返回一条数据
        wuhtt:@前前前前前端 我看了一下,之前歌单列表是从 textarea 里面拿 json 的,现在是个加了密的字符串 ,你的试试解密,不行只能解析网页了,good luck
        742e9663a858:@wuhtt 用了code1.js 可是歌单详情只返回10条数据
        wuhtt:嗯是的,但我记不清返回几条数据了,我当时用scrapy是加了个UserAgent的中间件的:yum:
      • Seeker_zz:请问 core.js 里相关的函数是怎么找的呀?单步调试的苦力活吗?
        wuhtt:@Seeker_zz 哈哈,还可以倒过来看啊。。。。不过的确有 相应的加密方式,然后还有人不是执行js ,是直接翻译成 Python 代码了
        Seeker_zz:@wuhtt :好的~smile: 偶然发现知乎也有分析这个接口的 https://www.zhihu.com/question/21471960
        看了 路人甲 的回答我再一看,这个window.asrsea 倒过来不就是Aes Rsa吗- -
        wuhtt:@Seeker_zz 搜索关键字,换着搜,比如参数里有xxxkey,你就搜key,这样慢慢找,也不用很久就能找到了。:yum:
      • Helios18:有没有成功,小编,直接来一个源码学习下啊
        wuhtt:我给了 core1.js 的下载地址,你看一下代码的截图,应该是可以做出来了:stuck_out_tongue:

      本文标题:网易云音乐Web端的接口分析

      本文链接:https://www.haomeiwen.com/subject/mdjfcxtx.html