在構造爬蟲請求時,通過瀏覽器抓包發現請求參數中帶了一個sign參數:
使用requests
包構造爬蟲請求,發現如果缺少這個sign字段或者sign計算的不正確,則只能得到“invalid signature”的服務端返回結果,無法得到正確的結果。因此還是只能研究一下sign是如何計算出來的。
通過觀察sign的字符串大致能夠猜到,是用了類似于md5的一種加密計算方式,加密用到的字段可能包括發送的messages,以及發送的“time”時間戳字段。之后,在服務端使用相同加密方式計算哈希值,如果結果一致才能正確返回結果,不一致則會返回錯誤。
要計算哈希值,肯定這段函數是放在js里的,所以我們在開發者工具中進入到“調試器”,然后全局搜索我們抓包的API路徑:
這樣就能定位到發送請求的那一段js代碼,而這段代碼中肯定就包含請求中的“sign”字段是從哪個變量取的,之后我們就可以看到“sign”字段的變量是如何被計算出來的。
為了能夠實現跳轉函數定義,我們可以把整個js文件拷貝到vscode中,然后格式化代碼,這樣讀起來更容易。定位到最終計算哈希值的函數:
可以看到sign字段是由時間戳+message字段計算的一個SHA256哈希值,為了能在爬蟲代碼中調用這段函數得到sign字段,我們需要安裝一個python第三方庫,用于在python代碼中執行js函數:
pip install PyExecJS
不過,我們需要先注意到,這段js函數是異步的(async),也就是如果直接調用函數的話,你其實拿不到任何返回值,js中想要獲取異步函數的值,都是通過函數執行完成之后的回調實現的,而我們要在python代碼中執行js的話,顯然是沒法回調的。所以我們最佳的辦法是不用異步函數,想辦法尋找同步函數作為替代。
分析這段js代碼可以看到,異步的地方主要在于crypto.subtle
包,所以上網一搜,有一個同步的函數也能達到同樣的效果,于是我們的js代碼就可以變為:
var crypto = require('crypto');
function sha256(content) {
// https://stackoverflow.com/questions/57626477/using-javascript-crypto-subtle-in-synchronous-function
return crypto.createHash('sha256').update(content).digest('hex')
}
function getSign(t, msg) {
n = {}.PUBLIC_SECRET_KEY,
a = `${t}:${msg}:${n}`;
sign = sha256(a)
return sign
};
之后,在python代碼中調用這段js函數,傳入時間戳和message參數,然后獲取函數返回結果:
import execjs
import time
js_sha = '''
var crypto = require('crypto');
function sha256(content) {
return crypto.createHash('sha256').update(content).digest('hex')
}
function getSign(t, msg) {
n = {}.PUBLIC_SECRET_KEY,
a = `${t}:${msg}:${n}`;
sign = sha256(a)
return sign
};
'''
time_now = int(time.time())
msg = "XXXX"
signature = execjs.compile(js_sha).call('getSign', time_now, msg)
之后,在requests請求的表單數據中加入計算得到的sign參數,就能得到服務端的正常返回了。