JAVA网易云热评接口_网易云音乐热评爬虫(一): 反编译加密参数
由于網(wǎng)易云音樂大部分請求做了混淆加密處理,因此直接用requests請求是行不通。本文以獲取歌曲全部評論為例,通過分析網(wǎng)易云的加密過程,來反編譯構(gòu)造加密參數(shù)。下面主要介紹下用Python實現(xiàn)加密參數(shù)的構(gòu)造,并獲取歌曲的全部評論。
一、了解加密過程
1.1 通過觀察網(wǎng)絡(luò)請求
發(fā)現(xiàn)如下接口包含所要的數(shù)據(jù)。包含該頁最新評論,以及所有熱評
再看它評論的參數(shù),著實讓人有點懵逼
很多人看見這一串字符,可能簡單看下前后接口有沒數(shù)據(jù)就放棄了。好了,不賣關(guān)子。這章分析下整個參數(shù)的加密過程,實現(xiàn)最終模擬請求拿到數(shù)據(jù)。
1.2 找請求參數(shù)
可以看的到,它的參數(shù)有兩個,一個是params,一個是encSecKey并且都是經(jīng)過加密的,我們就要分析它在js中的位置(F12打開source搜索encSecKey)
進(jìn)入js內(nèi)部,經(jīng)過斷點調(diào)試發(fā)現(xiàn)這里就是生成最終參數(shù)的地方
1.3 觀察參數(shù)變化
模擬不同分頁請求,觀察變化。效果如下圖:
對比參數(shù)變化,得出參數(shù)構(gòu)成:
csrf_token :始終空字符串
cursor :第一頁默認(rèn)-1;請求下一頁時該參數(shù)為上一頁最后一條評論時間戳(防止數(shù)據(jù)重復(fù))
offset :移動評論數(shù)
orderType :默認(rèn)1
pageNo :頁碼
pageSize :默認(rèn)20
rid : R_SO_4_ ?+ 歌曲ID
threadId :?R_SO_4_ ?+ 歌曲ID
自此,我們已經(jīng)知道整個參數(shù)的構(gòu)成,接下來就看下網(wǎng)易云是如何進(jìn)行參數(shù)加密的
二、分析加密函數(shù)
通過斷點已經(jīng)知道,下面這段js代碼為我們分析的重點
var bZj0x = window.asrsea(JSON.stringify(i2x), bkk0x(["流淚", "強"]), bkk0x(YS7L.md), bkk0x(["愛心", "女孩", "驚恐", "大笑"]));
e2x.data = j2x.cr3x({
params: bZj0x.encText,
encSecKey: bZj0x.encSecKey
})
只要把bZj0x解出來就ok了,這里主要分析幾個函數(shù)
2.1? JSON.stringify(i2x)
斷點調(diào)制找到i2x返回內(nèi)容
就是上文分析的整個字典參數(shù)
csrf_token: ""
cursor: "1610076350235"
offset: "40"
orderType: "1"
pageNo: "2"
pageSize: "20"
rid: "R_SO_4_1807537867"
threadId: "R_SO_4_1807537867"
2.2?bkk0x函數(shù)
bkk0x(["流淚", "強"]), bkk0x(YS7L.md), bkk0x(["愛心", "女孩", "驚恐", "大笑"]),都用到了同一個函數(shù),這里就看下bkk0x函數(shù)內(nèi)部實現(xiàn)是怎樣的
var bkk0x = function(cJj8b) {
var m2x = [];
j2x.bf2x(cJj8b, function(cJi8a) {
m2x.push(YS7L.emj[cJi8a])
});
return m2x.join("")
}
等同于python寫法
def get_bq_n1x(keys):
m0x = []
for key in keys:
m0x.append(emj[key])
return ''.join(m0x)
YS7L.emj是一個固定的字典
YS7L.emj = {
"色": "00e0b",
"流感": "509f6",
"這邊": "259df",
"弱": "8642d",
"嘴唇": "bc356",
"親": "62901",
"開心": "477df",
"呲牙": "22677",
"憨笑": "ec152",
"貓": "b5ff6",
"皺眉": "8ace6",
"幽靈": "15bb7",
"蛋糕": "b7251",
"發(fā)怒": "52b3a",
"大哭": "b17a8",
"兔子": "76aea",
"星星": "8a5aa",
"鐘情": "76d2e",
"牽手": "41762",
"公雞": "9ec4e",
"愛意": "e341f",
"禁止": "56135",
"狗": "fccf6",
"親親": "95280",
"叉": "104e0",
"禮物": "312ec",
"暈": "bda92",
"呆": "557c9",
"生病": "38701",
"鉆石": "14af6",
"拜": "c9d05",
"怒": "c4f7f",
"示愛": "0c368",
"汗": "5b7a4",
"小雞": "6bee2",
"痛苦": "55932",
"撇嘴": "575cc",
"惶恐": "e10b4",
"口罩": "24d81",
"吐舌": "3cfe4",
"心碎": "875d3",
"生氣": "e8204",
"可愛": "7b97d",
"鬼臉": "def52",
"跳舞": "741d5",
"男孩": "46b8e",
"奸笑": "289dc",
"豬": "6935b",
"圈": "3ece0",
"便便": "462db",
"外星": "0a22b",
"圣誕": "8e7",
"流淚": "01000",
"強": "1",
"愛心": "0CoJU",
"女孩": "m6Qyw",
"驚恐": "8W8ju",
"大笑": "d"
}
YS7L.md是一個固定的數(shù)據(jù)
YS7L.md = ["色", "流感", "這邊", "弱", "嘴唇", "親", "開心", "呲牙", "憨笑", "貓", "皺眉", "幽靈", "蛋糕", "發(fā)怒", "大哭", "兔子", "星星", "鐘情", "牽手", "公雞", "愛意", "禁止", "狗", "親親", "叉", "禮物", "暈", "呆", "生病", "鉆石", "拜", "怒", "示愛", "汗", "小雞", "痛苦", "撇嘴", "惶恐", "口罩", "吐舌", "心碎", "生氣", "可愛", "鬼臉", "跳舞", "男孩", "奸笑", "豬", "圈", "便便", "外星", "圣誕"]
所以整個window.asrsea的參數(shù)都是可以得到的了,下面看下 window.asrsea()查看這個函數(shù)執(zhí)行了什么操作
2.3?window.asrsea()
function a(a) {
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; a > d; d += 1)
e = Math.random() * b.length,
e = Math.floor(e),
c += b.charAt(e);
return c
}
function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b)
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, {
iv: d,
mode: CryptoJS.mode.CBC
});
return f.toString()
}
function c(a, b, c) {
var d, e;
return setMaxDigits(131),
d = new RSAKeyPair(b,"",c),
e = encryptedString(d, a)
}
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
}
function e(a, b, d, e) {
var f = {};
return f.encText = c(a + e, b, d),
f
}
window.asrsea = d
可以看到window.asrsea = d,所以我們要執(zhí)行的就是d這個函數(shù),主要執(zhí)行3個操作
a(16),生成16位隨機數(shù)
進(jìn)行兩次AES加密得到h.encText
通過位移等一系列運算生成h.encSecKey
三、Python實現(xiàn)相同的加密算法
"""
網(wǎng)易云請求參數(shù)反編譯工具
:主要斷點觀察js,改為python實現(xiàn)
"""
emj = {
"色": "00e0b",
"流感": "509f6",
"這邊": "259df",
"弱": "8642d",
"嘴唇": "bc356",
"親": "62901",
"開心": "477df",
"呲牙": "22677",
"憨笑": "ec152",
"貓": "b5ff6",
"皺眉": "8ace6",
"幽靈": "15bb7",
"蛋糕": "b7251",
"發(fā)怒": "52b3a",
"大哭": "b17a8",
"兔子": "76aea",
"星星": "8a5aa",
"鐘情": "76d2e",
"牽手": "41762",
"公雞": "9ec4e",
"愛意": "e341f",
"禁止": "56135",
"狗": "fccf6",
"親親": "95280",
"叉": "104e0",
"禮物": "312ec",
"暈": "bda92",
"呆": "557c9",
"生病": "38701",
"鉆石": "14af6",
"拜": "c9d05",
"怒": "c4f7f",
"示愛": "0c368",
"汗": "5b7a4",
"小雞": "6bee2",
"痛苦": "55932",
"撇嘴": "575cc",
"惶恐": "e10b4",
"口罩": "24d81",
"吐舌": "3cfe4",
"心碎": "875d3",
"生氣": "e8204",
"可愛": "7b97d",
"鬼臉": "def52",
"跳舞": "741d5",
"男孩": "46b8e",
"奸笑": "289dc",
"豬": "6935b",
"圈": "3ece0",
"便便": "462db",
"外星": "0a22b",
"圣誕": "8e7",
"流淚": "01000",
"強": "1",
"愛心": "0CoJU",
"女孩": "m6Qyw",
"驚恐": "8W8ju",
"大笑": "d"
}
md = ["色", "流感", "這邊", "弱", "嘴唇", "親", "開心", "呲牙", "憨笑", "貓", "皺眉", "幽靈", "蛋糕", "發(fā)怒", "大哭", "兔子", "星星", "鐘情", "牽手",
"公雞", "愛意", "禁止", "狗", "親親", "叉", "禮物", "暈", "呆", "生病", "鉆石", "拜", "怒", "示愛", "汗", "小雞", "痛苦", "撇嘴", "惶恐", "口罩",
"吐舌", "心碎", "生氣", "可愛", "鬼臉", "跳舞", "男孩", "奸笑", "豬", "圈", "便便", "外星", "圣誕"]
def get_bq_n1x(keys):
m0x = []
for key in keys:
m0x.append(emj[key])
return ''.join(m0x)
def __get_random_str():
"""
Returns:16位的隨機字符串
"""
str_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
random_str = ""
for i in range(16):
index = random.randint(0, len(str_set) - 1)
random_str += str_set[index]
return random_str
arg2 = get_bq_n1x(["流淚", "強"])
arg3 = get_bq_n1x(md)
arg4 = get_bq_n1x(["愛心", "女孩", "驚恐", "大笑"])
random_str = __get_random_str()
def __aes_encrypt(text, key):
"""
獲取到ASW加密后的數(shù)據(jù)
Args:
text: 首先CBC加密方法,text必須位16位數(shù)據(jù)
key: 加密的key
Returns:加密后的字符串
"""
# 加密或者解密的初始向量(16位)
iv = "0102030405060708"
# 不是16的倍數(shù)則填充
pad = 16 - len(text) % 16
if isinstance(text, str):
text = text + pad * chr(pad)
else:
text = text.deocde("utf-8") + pad * chr(pad)
aes = AES.new(key=bytes(key, encoding="utf-8"), mode=2, iv=bytes(iv, encoding="utf-8"))
res = aes.encrypt(bytes(text, encoding="utf-8"))
res = base64.b64encode(res).decode("utf-8")
return res
def __get_enc_text(arg1):
"""
對稱加密后的參數(shù)
Args:
arg1:加密參數(shù)
Returns:
"""
enc_text = __aes_encrypt(arg1, arg4)
enc_text = __aes_encrypt(enc_text, random_str)
return enc_text
def __get_enc_sec_key():
"""
對稱加密密鑰
通過查看js代碼,獲取encSecKey
"""
# 隨機字符串逆序排列
text = random_str[::-1]
rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(arg2, 16) % int(arg3, 16)
return format(rs, 'x').zfill(256)
def linux_encrypt(text):
# print(text)
return text
def get_form_data(text, method=''):
"""
反編譯生成
請求的form-data參數(shù)
Args:
text: 跟蹤js,自己組裝參數(shù)
method: 方法
Returns:form-data參數(shù)
"""
"""
"""
if method == 'linux':
return linux_encrypt(text)
text = str(text)
return {"params": __get_enc_text(text), "encSecKey": __get_enc_sec_key()}
四、模擬請求歌曲評論
只要加密過程知道了,其實很多接口都可以模擬請求,此文僅供學(xué)習(xí)。
最終執(zhí)行效果如下圖:
總結(jié)
以上是生活随笔為你收集整理的JAVA网易云热评接口_网易云音乐热评爬虫(一): 反编译加密参数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习数据集的准备
- 下一篇: OSChina 周五乱弹 ——和2.1米