这个网易云JS解密,老网抑云看了都直呼内行

最近更新频率慢了,这不是因为CK3发售了嘛,一个字就是“肝”。今天来看一下网易云音乐两个加密参数 params encSecKey ,顺便抓取一波某歌单的粉丝,有入库哦,使用 mysql 存储,觉得有帮助的别忘了关注一下公众号啊,完整的JS代码都已整理好,请关注 知识图谱与大数据 公众号,找到本文点击文末 阅读更多 获取。我的文章一般都有完整代码,创作不易啊,大家请多关注啊,当然不关注想白票也无所谓。

目标

网易云音乐只需要解密 params 和 encSecKey 就可以开始快乐的抓取了,当然你没有足够的代理IP的话,还是要慢点爬,废话不多说,直接开整。

请求

老规矩,先看看我们要抓的页面样子: 
在这里插入图片描述
查看网络请求: 
在这里插入图片描述
从名字上就可以快速定位是哪个请求,是 POST 请求,那看看它提交了哪些参数吧, FormData 如下: 
在这里插入图片描述
提交的参数即前面提过的 params 和 encSecKey ,是加密过的,看看返回的内容的格式: 
在这里插入图片描述
ok,基本的东西我们都已经知道,可以进入下一步了找到解密两个参数了。

分析

调试前我们得先找到这两个值的位置,那就搜索呗,先定位 JS 文件再定位代码位置。怎么搜索应该都知道的吧,搜 params 或 encSecKey均可,搜出来多个结果不确定是哪个文件的话可以每个点进去再搜索一下关键参数,以此确定是否是目标文件,这里我直接标记了正确文件,大家点击进去即可。 
在这里插入图片描述
进入JS文件后,同样搜索关键参数 params 或 encSecKey : 
在这里插入图片描述
找到了 encSecKey 的位置,把这几行代码抠出来分析一下:

                         var bVZ8R = window.asrsea(JSON.stringify(i0x), bqN0x(["流泪", "强"]), bqN0x(Wx5C.md), bqN0x(["爱心", "女孩", "惊恐", "大笑"]));
            e0x.data = j0x.cs1x({
                params: bVZ8R.encText,
                encSecKey: bVZ8R.encSecKey
            })  

粗略看, params 和 encSecKey 来自 bVZ8R.encText 和 bVZ8R.encSecKey ,而 bVZ8R 是 window.asrsea 的结果, window.asrsea 有四个参数, JSON.stringify(i0x), bqN0x(["流泪", "强"]), bqN0x(Wx5C.md), bqN0x(["爱心", "女孩", "惊恐", "大笑"] ,先看后面三个参数,从它们固定的值可以大胆推测这三个值也是固定的。之所以说都是固定的,看看 Wx5C.md : 
在这里插入图片描述
Wx5C.md 是一个固定好的数组,而 bqN0x(["流泪", "强"]) 和 bqN0x(["爱心", "女孩", "惊恐", "大笑"] 这结果肯定也是不会变的,如下图,测试了一下:

在这里插入图片描述
大概弄清楚了这几个参数,剩下的就是搞明白 window.asrsea 的具体实现方式,还有 i0x 是什么样的,进入调试环节。

调试

window.asrsea 打上断点,我的代码位置是 13133 行,点击粉丝列表下一页就会激活断点,在激活断点的同时我们也能一睹 i0x 的芳容,在控制台中输入 i0x : 
在这里插入图片描述

limit、offset、total、userId 这些其实都是可知的,而 csrf_token 的产生可以看这里,细心的童靴应该早就发现了:

                         }
            i0x["csrf_token"] = v0x.gP3x("__csrf"); ## csrf_token在这里产生
            X0x = X0x.replace("api", "weapi");
            e0x.method = "post";
            delete e0x.query;
            var bVZ8R = window.asrsea(JSON.stringify(i0x), bqN0x(["流泪", "强"]), bqN0x(Wx5C.md), bqN0x(["爱心", "女孩", "惊恐", "大笑"]));  

我点进 v0x.gP3x 函数看了看: 
在这里插入图片描述 
从代码中可以看出 csrf_token 来自于 Cookie 中的 __csrf 
在这里插入图片描述 
那这个值就可以在请求网页的时候从cookie中获取到,继续调试 window.asrsea 吧。一路点击下一步,进入函数中。 
在这里插入图片描述
跳到一个 d(d,e,f,g) 函数里,稍微往下一看,发现 window.asrsea 就等于这个 函数,哦了,那就调试这个 函数就行:

                         function d(d, e, f, g) {
        var h = {}
          , i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }  

进入 函数: 
在这里插入图片描述
大概看出来 函数是产生随机数的,继续运行,进入 函数: 
在这里插入图片描述
熟悉的 AES 加密,继续运行进入 函数:

在这里插入图片描述
又是熟悉的 RSA 加密,网易可真谨慎,各种加密。到这里总的框架已经调试完了,剩下的无非就是抠 JS 代码了。

python运行

这次不是单单运行了结果哦,还带上了爬取与入库:

获取 params 和 encSecKey

                         def get_enc(self,a):
        with open('..//js//wangyiyun.js', encoding='utf-8') as f:
            wangyiyun = f.read()
        js = execjs.compile(wangyiyun)
        logid = js.call('get_pwd', a)
        print(logid)
        return logid  

抓取

                         def get_fans(self):
        resp = self.get_home_page()
        print(resp.cookies)
        print(resp.status_code)
        time.sleep(6)
        limit = 20
        for i in range(1,110):
            print("第{}页".format(i+1))
            offset = limit*i
            a = {"userId": "46991111", "offset": str(offset), "total": "false", "limit": str(limit), "csrf_token": ""}
            print(a)
            logid = self.get_enc(a)
            data = {
                "params":logid["encText"],
                "encSecKey":logid["encSecKey"],
            }
            print(data)
            fans_url = "https://music.163.com/weapi/user/getfolloweds?csrf_token="
            resp = self.session.post(url=fans_url,data=data,headers=self.headers)
            followed = json.loads(resp.text)
            followed_list = []
            for foll in followed["followeds"]:
                foll_dict = {}
                foll_dict["short_name"] = foll.get("py","") #缩写
                foll_dict["userId"] = foll.get("userId","") #用户ID
                foll_dict["nickname"] = foll.get("nickname","") #昵称
                foll_dict["vipType"] = foll.get("vipType","") # vip
                foll_dict["eventCount"] = foll.get("eventCount","")#动态
                foll_dict["vipRights"] = str(foll.get("vipRights","")) #VIP权益
                foll_dict["gender"] = foll.get("gender","") #性别
                foll_dict["avatarUrl"] = foll.get("avatarUrl","") #头像
                foll_dict["followed"] = foll.get("followed","")
                foll_dict["followeds"] = foll.get("followeds","") #粉丝
                foll_dict["follows"] = foll.get("follows","")  #关注
                foll_dict["playlistCount"] = foll.get("playlistCount","") #歌单
                foll_dict["mutual"] = foll.get("mutual","") #
                foll_dict["expertTags"] = str(foll.get("expertTags",""))
                foll_dict["experts"] = str(foll.get("experts",""))
                print(foll_dict)
                followed_list.append(foll_dict)
            self.mysql.insert("music",followed_list)
            tm = random.randint(10,30)
            time.sleep(tm)  

这里要注意一下,要抓取指定的页面你还得先访问这个页面,不能直接请求 "https://music.163.com/weapi/user/getfolloweds?csrf_token= 这个链接,因为它根本就没有带关于哪个页面的信息。

请求指定网页

                         def get_home_page(self):
        url = "https://music.163.com/#/user/home?id=1737833656"
        resp = self.session.get(url)
        return resp  

表结构

                         @property
    def create_table_sql(self):
        create_table = """
                CREATE TABLE IF NOT EXISTS music (
                short_name varchar(30) ,     
                userId varchar(100) NOT NULL,     
                nickname varchar(30),     
                vipType varchar(30) ,     
                eventCount varchar(200),
                vipRights varchar(900),
                gender varchar(900),
                avatarUrl varchar(200),
                followed varchar(30),
                followeds varchar(30),
                follows varchar(30),
                playlistCount varchar(30),
                mutual varchar(30),
                expertTags varchar(30),
                experts varchar(30),
                PRIMARY KEY (userId)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""
        return create_table  

入库

                         def insert(self,table,data_list):
        if len(data_list) > 0:
            data_list = [{k: v
                          for k, v in data.items() if v is not None}
                         for data in data_list]

            keys = ", ".join(data_list[0].keys())
            values = ", ".join(["%s"] * len(data_list[0]))
        sql = """INSERT INTO {table}({keys}) VALUES ({values}) ON
                    DUPLICATE KEY UPDATE""".format(table=table,
                                                   keys=keys,
                                                   values=values)
        update = ",".join([
            " {key} = values({key})".format(key=key)
            for key in data_list[0]
        ])
        sql += update
        print(sql)
        self.connect()
        try:
            ret = self.cursor.executemany(sql, [tuple(data.values()) for data in data_list])
            self.conn.commit()
        except Exception as e:
            self.conn.rollback()
            print("Error: ", e)
            traceback.print_exc()
        finally:
            self.close()  

过程

在这里插入图片描述

结束

快乐的时光过得真快,到这里就结束了,抓取过程与入库大家可以作为一个参考。完整的 JS 代码都已整理好,请关注 知识图谱与大数据公众号,找到本文点击文末 阅读更多 获取,创作不易,请多关注,当然不关注也无所谓。 
在这里插入图片描述

posted @ 2020-10-10 16:15  小琪琪来啦  阅读(368)  评论(0编辑  收藏  举报
欢迎大家来到我的博客:武汉雅思 | 武汉托福 | 新航道 | 雅思培训 | dnfsf | 天龙sf | 热血江湖sf | 天龙sf | dnfsf