網易雲爬蟲爬取用戶粉絲信息


網易雲爬蟲爬取用戶粉絲信息

0x01 前言

前不久聽說女神挺喜歡小狗小貓的,就給女神發了些小貓圖片和視頻,女神於是就給推薦了個網易雲的博主。

發現女神平常用網易雲聽歌,就突想知道平常女神都喜歡聽一些什么歌。之前有見女神分享自己網易雲圖片,隱約記得賬戶名中有‘chocolate’,就登上網易雲一通搜索,一個一個用戶信息挨着去看,也沒發現符合的;突然靈機一閃,女神不是也關注了之前給分享的那個“萌寵圖刊”的賬戶嗎,可以去他的粉絲里去找啊!說干就干,打開之后,emmm……將近四萬的粉絲,一個一個去翻要到猴年馬月了……直接寫個爬蟲爬取粉絲用戶名再搜索吧!

0x02 網站內容分析

打開網易雲網站,發現不需要登錄就能搜索查看用戶粉絲,發現簡單了不少,不用關心登錄了!

搜索“萌寵圖刊”然后點擊粉絲,F12,挨着一個一個查看響應內容,很快就找到了我們需要的內容:

然后雙擊打開,確實為我們需要的響應包:

查看其請求內容,提交了兩個參數,明顯為加密之后的:

先嘗試獲得第一頁的粉絲昵稱!

0x03 爬取單頁粉絲昵稱

 1 import urllib.request
 2 import urllib.parse
 3 import json
 4 
 5 headers = {
 6     "Referer": "http://music.163.com",
 7     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4350.7 Safari/537.36",
 8 }
 9 url = "https://music.163.com/weapi/user/getfolloweds?csrf_token="
10 
11 formdata = {
12     "params": "DxYWvntrh5oSYJ0o3vIBh/q/zJSFyiqQ1jAWx/B9tH5j6RdTabrnEP1WCj3wmx1l+WfGB0FQh3L3/lqcoozHZRXSMMqF+thJcWpR7Wwq53EGDa4XSiKhnBWSUdHBq9Io9H8WUmXBRVoa/3O4yG5gKg==",
13     "encSecKey": "bbee09bca20f8a7f73a9d6ecf4e6f1a008235e696fd2f1e756a05b94a6a936ff6ee50b0a8dda8b9cf3ac5d3a1fcc231afbc2585b7fbfc6c4751a14f989ddd44c5f76c9516921173c44c3d844064a07a49f615494501e227d57ead86a058d73bf99260be7e6cb92fea65e56174c2dd63e1a93322d655bc654f728bbab6eafac78"
14 }
15 
16 temp_data = json.loads(urllib.request.urlopen(urllib.request.Request(url=url, data=urllib.parse.urlencode(formdata).encode("utf-8"),headers=headers)).read().decode("utf-8"))["followeds"]
17 
18 for eve_data in temp_data:
19     nickname = eve_data["nickname"]
20     print(nickname)

 

運行結果正常

0x04 分析加密參數

由於獲取粉絲信息是由兩個請求參數獲得的,要對這兩個參數進行分析。

查看不同頁數兩個請求參數值內容不同,因此可以確定翻頁功能確實是由這兩個參數控制的!

這兩個參數很明顯是經過加密之后的,而這個請求又與一個js文件息息相關,因此判斷有可能是請求數據通過這個js加密之后再發出請求的,打開這個js文件格式化后進行分析,對兩個參數進行搜索。

通過分析發現最終所傳遞的參數應該就是從這個地方來的,想要知道他到底傳的參數是什么內容,我可以對這個js進行簡單的修改,即在這兩個語句之前加一個alert語句,讓 bZj0x 內的四個變量進行彈出:

1 alert("wyy" + JSON.stringify(i2x) + "||" + bkk0x(["流淚", "強"]) + "||" + bkk0x(YS7L.md) + "||" + bkk0x(["愛心", "女孩", "驚恐", "大笑"]))

然后利用 charles 軟件,將原本網站的js文件用修改過的文件進行替換,再次訪問粉絲頁面

訪問第一頁:

訪問第二頁:

分析彈窗的內容:

wyy{"userId":"30982607","offset":"0","total":"true","limit":"20","csrf_token":""}||010001||00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7||0CoJUm6Qyw8W8jud

wyy{"userId":"30982607","offset":"20","total":"false","limit":"20","csrf_token":""}||010001||00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7||0CoJUm6Qyw8W8jud

 

可以看出,四個參數用 “||” 隔開,且只有第一個參數有變化,其他三個參數為常量。

其中 “userId” 為該用戶的id,“offset” 發生了變化,且剛好對應每一頁粉絲個數為20.因此 “offset“ 用來控制翻頁。

接下來就是重新回到js文件中,查看加密的方法。

0x05 加密流程分析

在js文件中重新查找encText 和 encSecKey

可以看出,加密部分就是在這里。

 1   function a(a) {
 2         var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
 3         for (d = 0; a > d; d += 1) e = Math.random() * b.length, e = Math.floor(e), c += b.charAt(e);
 4         return c
 5     }
 6 
 7     function b(a, b) {
 8         var c = CryptoJS.enc.Utf8.parse(b), d = CryptoJS.enc.Utf8.parse("0102030405060708"),
 9             e = CryptoJS.enc.Utf8.parse(a), f = CryptoJS.AES.encrypt(e, c, {iv: d, mode: CryptoJS.mode.CBC});
10         return f.toString()
11     }
12 
13     function c(a, b, c) {
14         var d, e;
15         return setMaxDigits(131), d = new RSAKeyPair(b, "", c), e = encryptedString(d, a)
16     }
17 
18     function d(d, e, f, g) {
19         var h = {}, i = a(16);
20         return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h
21     }
22 
23     function e(a, b, d, e) {
24         var f = {};
25         return f.encText = c(a + e, b, d), f
26     }

 根據相同的加密流程,進行處理。

 1 def get_params_first(userId, offset):
 2     params_first = '{"userId":"' + userId + '","offset":"' + str(offset) + '","total":"false","limit":"100","csrf_token":"6f033a3138de5a06cea24807ba88e40b"}'
 3     return params_first
 4 #userid 是用戶id    offset 是控制翻頁
 5 #params_first = '{"userId":"30982607","offset":"20","total":"false","limit":"20","csrf_token":""}'
 6 params_second = "010001"
 7 params_thrid = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
 8 params_forth = "0CoJUm6Qyw8W8jud"
 9 
10 #params 需要第一個參數和第四個參數
11 #encSecKey 需要第二個和第三個參數,還需要一個隨機的16個字符串
12 
13 def aesEncrypt(text, key):
14     # 文本
15     pad = 16 - len(text) % 16
16     text = text + pad * chr(pad)
17     key = key.encode('utf-8')
18     encryptor = AES.new(key, 2, b'0102030405060708')
19     ciphertext = encryptor.encrypt(text.encode('utf-8'))
20     ciphertext = base64.b64encode(ciphertext)
21     return ciphertext
22 
23 def get_params(text, userId, offset):
24     # 第一個參數
25     params_first = get_params_first(userId, offset)
26     params = aesEncrypt(params_first, params_forth).decode('utf-8')
27     params = aesEncrypt(params, text)
28     return params
29 
30 def rsaEncrypt(pubKey, text, modulus):
31     #進行rsa加密
32     text = text[::-1]
33     rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)
34     return format(rs, 'x').zfill(256)
35 
36 def get_encSecKey(text):
37     pubKey = params_second
38     moudulus = params_thrid
39     encSecKey = rsaEncrypt(pubKey, text, moudulus)
40     return encSecKey

 

至此,加密部分已經完成,可以通過控制“offset”的數值來控制翻頁。

0x06 最終完整代碼

 1 import urllib.request
 2 import urllib.parse
 3 import json
 4 import base64
 5 from Crypto.Cipher import AES
 6 import codecs
 7 
 8 headers = {
 9     "Referer": "http://music.163.com",
10     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4350.7 Safari/537.36",
11 }
12 url = "https://music.163.com/weapi/user/getfolloweds?csrf_token="
13 
14 def get_params_first(userId, offset):
15     params_first = '{"userId":"' + userId + '","offset":"' + str(offset) + '","total":"false","limit":"20","csrf_token":"6f033a3138de5a06cea24807ba88e40b"}'
16     return params_first
17 #userid 是視頻的標志    offset 是控制翻頁
18 #params_first = '{"userId":"30982607","offset":"20","total":"false","limit":"20","csrf_token":""}'
19 params_second = "010001"
20 params_thrid = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
21 params_forth = "0CoJUm6Qyw8W8jud"
22 
23 #params 需要第一個參數和第四個參數
24 #encSecKey 需要第二個和第三個參數,還需要一個隨機的16個字符串
25 
26 def aesEncrypt(text, key):
27     # 文本
28     pad = 16 - len(text) % 16
29     text = text + pad * chr(pad)
30     key = key.encode('utf-8')
31     encryptor = AES.new(key, 2, b'0102030405060708')
32     ciphertext = encryptor.encrypt(text.encode('utf-8'))
33     ciphertext = base64.b64encode(ciphertext)
34     return ciphertext
35 
36 def get_params(text, userId, offset):
37     # 第一個參數
38     params_first = get_params_first(userId, offset)
39     params = aesEncrypt(params_first, params_forth).decode('utf-8')
40     params = aesEncrypt(params, text)
41     return params
42 
43 def rsaEncrypt(pubKey, text, modulus):
44     #進行rsa加密
45     text = text[::-1]
46     rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)
47     return format(rs, 'x').zfill(256)
48 
49 def get_encSecKey(text):
50     pubKey = params_second
51     moudulus = params_thrid
52     encSecKey = rsaEncrypt(pubKey, text, moudulus)
53     return encSecKey
54 
55 userId = "30982607"
56 offset = 0
57 path = "./" + userId + ".txt"
58 while True:
59     text = "A"* 16
60     params = get_params(text, userId, offset)
61     encSecKey = get_encSecKey(text)
62     formdata = {
63         "params": params,
64         "encSecKey": encSecKey
65     }
66     temp_data = json.loads(urllib.request.urlopen(urllib.request.Request(url=url, data=urllib.parse.urlencode(formdata).encode("utf-8"), headers=headers)).read().decode("utf-8"))["followeds"]
67     length = len(temp_data)
68 
69     for eve_data in temp_data:
70         nickname = eve_data["nickname"]
71         print(nickname)
72         with open(path, "a") as file:
73             file.write(nickname + "\n")
74     if offset < 38168:
75         offset = offset + 20
76         #print(offset)
77     else:
78         break

 

 0x07 杯具

運行了半天才發現,只能爬到最近關注的1000個粉絲。。。同樣,網站上也是只能查看50頁。。。也就是“offset“的值最大為1000

不死心的我,再次看了下第一個參數,發現還有一個“limit“字段,於是嘗試改變limit值,發現其控制的是一頁顯示粉絲的數量,但是經過測試,其上限為100

改變“limit”值再次運行,發現一次能爬取100個粉絲昵稱,速度大大提高,但也只能夠爬取到1100個粉絲昵稱。

 

既然網站只能查看50頁的粉絲,pc客戶端呢?

看到638頁,感覺有戲!直接點擊最后一頁,然后,emmmm。。。

再去看手機app,還好,並沒有被限制。。。

只能去一點點手動翻了,只能慶幸粉絲只有3w多了。。。


免責聲明!

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



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