這個網易雲JS解密,老網抑雲看了都直呼內行
阿新 • • 發佈:2020-10-10
> 最近更新頻率慢了,這不是因為CK3發售了嘛,一個字就是“肝”。今天來看一下網易雲音樂兩個加密引數**params**和**encSecKey**,順便抓取一波某歌單的粉絲,有入庫哦,使用**mysql**儲存,覺得有幫助的別忘了關注一下公眾號啊,完整的JS程式碼都已整理好,請關注**知識圖譜與大資料**公眾號,找到本文點選文末**閱讀更多**獲取。我的文章一般都有完整程式碼,創作不易啊,大家請多關注啊,當然不關注想白票也無所謂。
## 目標
網易雲音樂只需要解密`params`和`encSecKey`就可以開始快樂的抓取了,當然你沒有足夠的代理IP的話,還是要慢點爬,廢話不多說,直接開整。
## 請求
老規矩,先看看我們要抓的頁面樣子:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907205617152.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
檢視網路請求:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907205700502.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
從名字上就可以快速定位是哪個請求,是`POST`請求,那看看它提交了哪些引數吧,`FormData`如下:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907205826205.png#pic_center)
提交的引數即前面提過的`params`和`encSecKey`,是加密過的,看看返回的內容的格式:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907210006639.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
ok,基本的東西我們都已經知道,可以進入下一步了找到解密兩個引數了。
## 分析
除錯前我們得先找到這兩個值的位置,那就搜尋唄,先定位`JS`檔案再定位程式碼位置。怎麼搜尋應該都知道的吧,搜`params`或`encSecKey`均可,搜出來多個結果不確定是哪個檔案的話可以每個點進去再搜尋一下關鍵引數,以此確定是否是目標檔案,這裡我直接標記了正確檔案,大家點選進去即可。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907210435897.png#pic_center)
進入JS檔案後,同樣搜尋關鍵引數`params`或`encSecKey`:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907210952969.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
找到了`encSecKey`的位置,把這幾行程式碼摳出來分析一下:
```python
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`:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907213005323.png#pic_center)
`Wx5C.md`是一個固定好的陣列,而`bqN0x(["流淚", "強"])`和`bqN0x(["愛心", "女孩", "驚恐", "大笑"]`這結果肯定也是不會變的,如下圖,測試了一下:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907213205439.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
大概弄清楚了這幾個引數,剩下的就是搞明白`window.asrsea`的具體實現方式,還有`i0x`是什麼樣的,進入除錯環節。
## 除錯
`window.asrsea`打上斷點,我的程式碼位置是`13133`行,點選粉絲列表下一頁就會啟用斷點,在啟用斷點的同時我們也能一睹`i0x`的芳容,在控制檯中輸入`i0x`:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907213704621.png#pic_center)
`limit、offset、total、userId`這些其實都是可知的,而`csrf_token`的產生可以看這裡,細心的童靴應該早就發現了:
```python
}
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`函式看了看:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907214134668.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
從程式碼中可以看出`csrf_token`來自於`Cookie`中的`__csrf`:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907214231800.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
那這個值就可以在請求網頁的時候從cookie中獲取到,繼續除錯`window.asrsea`吧。一路點選下一步,進入函式中。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907214523253.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
跳到一個`d(d,e,f,g)`函式裡,稍微往下一看,發現`window.asrsea`就等於這個`d`函式,哦了,那就除錯這個`d`函式就行:
```javascript
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`函式:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907214909895.png#pic_center)
大概看出來`a`函式是產生隨機數的,繼續執行,進入`b`函式:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020090721465099.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
熟悉的`AES`加密,繼續執行進入`c`函式:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907215132299.png#pic_center)
又是熟悉的`RSA`加密,網易可真謹慎,各種加密。到這裡總的框架已經除錯完了,剩下的無非就是摳`JS`程式碼了。
## python執行
這次不是單單運行了結果哦,還帶上了爬取與入庫:
#### 獲取`params`和`encSecKey`
```python
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
```
#### 抓取
```python
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=`這個連結,因為它根本就沒有帶關於哪個頁面的資訊。
#### 請求指定網頁
```python
def get_home_page(self):
url = "https://music.163.com/#/user/home?id=1737833656"
resp = self.session.get(url)
return resp
```
#### 表結構
```python
@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
```
#### 入庫
```python
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()
```
#### 過程
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907220119797.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)
## 結束
快樂的時光過得真快,到這裡就結束了,抓取過程與入庫大家可以作為一個參考。完整的`JS`程式碼都已整理好,請關注**知識圖譜與大資料**公眾號,找到本文點選文末**閱讀更多**獲取,創作不易,請多關注,當然不關注也無所謂。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200907220332356.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b2h1YTEzOTUw,size_16,color_FFFFFF,t_70#pic_center)