這個網易雲JS解密,老網抑雲看了都直呼內行


最近更新頻率慢了,這不是因為CK3發售了嘛,一個字就是“肝”。今天來看一下網易雲音樂兩個加密參數paramsencSecKey,順便抓取一波某歌單的粉絲,有入庫哦,使用mysql存儲,覺得有幫助的別忘了關注一下公眾號啊,完整的JS代碼都已整理好,請關注知識圖譜與大數據公眾號,找到本文點擊文末閱讀更多獲取。我的文章一般都有完整代碼,創作不易啊,大家請多關注啊,當然不關注想白票也無所謂。

目標

網易雲音樂只需要解密paramsencSecKey就可以開始快樂的抓取了,當然你沒有足夠的代理IP的話,還是要慢點爬,廢話不多說,直接開整。

請求

老規矩,先看看我們要抓的頁面樣子:
在這里插入圖片描述
查看網絡請求:
在這里插入圖片描述
從名字上就可以快速定位是哪個請求,是POST請求,那看看它提交了哪些參數吧,FormData如下:
在這里插入圖片描述
提交的參數即前面提過的paramsencSecKey,是加密過的,看看返回的內容的格式:
在這里插入圖片描述
ok,基本的東西我們都已經知道,可以進入下一步了找到解密兩個參數了。

分析

調試前我們得先找到這兩個值的位置,那就搜索唄,先定位JS文件再定位代碼位置。怎么搜索應該都知道的吧,搜paramsencSecKey均可,搜出來多個結果不確定是哪個文件的話可以每個點進去再搜索一下關鍵參數,以此確定是否是目標文件,這里我直接標記了正確文件,大家點擊進去即可。
在這里插入圖片描述
進入JS文件后,同樣搜索關鍵參數paramsencSecKey
在這里插入圖片描述
找到了encSecKey的位置,把這幾行代碼摳出來分析一下:

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

粗略看,paramsencSecKey來自bVZ8R.encTextbVZ8R.encSecKey,而bVZ8Rwindow.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就等於這個d函數,哦了,那就調試這個d函數就行:

    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
    }

進入a函數:
在這里插入圖片描述
大概看出來a函數是產生隨機數的,繼續運行,進入b函數:
在這里插入圖片描述
熟悉的AES加密,繼續運行進入c函數:

在這里插入圖片描述
又是熟悉的RSA加密,網易可真謹慎,各種加密。到這里總的框架已經調試完了,剩下的無非就是摳JS代碼了。

python運行

這次不是單單運行了結果哦,還帶上了爬取與入庫:

獲取paramsencSecKey

    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代碼都已整理好,請關注知識圖譜與大數據公眾號,找到本文點擊文末閱讀更多獲取,創作不易,請多關注,當然不關注也無所謂。
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM