別讓加密難倒你:Python爬蟲攻克加密網(wǎng)站的實(shí)戰(zhàn)教程
今天有個(gè)朋友向我求助,希望我?guī)退廊∫粋€(gè)網(wǎng)站上的內(nèi)容。網(wǎng)站內(nèi)容如下:
aHR0cHM6Ly93d3cuY2NwcmVjLmNvbS9uYXZDcXpyLyMvCg==
打開上述網(wǎng)址,進(jìn)入開發(fā)者模式,這些數(shù)據(jù)的請(qǐng)求接口,正常邏輯是通過搜索頁面上內(nèi)容進(jìn)行鎖定請(qǐng)求接口。但是,進(jìn)行搜索時(shí),發(fā)現(xiàn)什么都搜索不到。
通過上圖發(fā)現(xiàn),這些數(shù)據(jù)只請(qǐng)求了一個(gè)接口,當(dāng)我點(diǎn)擊負(fù)載和響應(yīng)這兩個(gè)標(biāo)簽,發(fā)現(xiàn)數(shù)據(jù)是被加密了,所以,我們搜索不了頁面的內(nèi)容。
負(fù)載加密內(nèi)容
響應(yīng)加密內(nèi)容
瀏覽器為啥是明文?
這時(shí)相信小伙伴,心中都有一個(gè)疑問瀏覽器為啥是明文? 瀏覽器在收到數(shù)據(jù)后,會(huì)自動(dòng)采用服務(wù)器返回的資源文件對(duì)加密內(nèi)容進(jìn)行解密并顯示明文,這也是我們能夠在頁面上看到正常內(nèi)容的原因。
如何定位到加密資源文件
通過XHR/提取斷點(diǎn),該方法是通過匹配URL包括請(qǐng)求路徑關(guān)鍵字進(jìn)行斷點(diǎn),具體配置如下圖:
這時(shí)我們重新刷新網(wǎng)頁,如下圖所示,網(wǎng)站就成功進(jìn)入我們上一步設(shè)置的斷點(diǎn)中。
其中,h就是加密后的請(qǐng)求體參數(shù),然后我們從調(diào)用堆棧中一步步往前推。至于如何找呢?這里是有一個(gè)技巧的,我們需要找到前一步請(qǐng)求體還沒加密,后一步請(qǐng)求體就加密成功了。
請(qǐng)求體加密后
請(qǐng)求體加密前
其中上圖o就是請(qǐng)求體加密之前的參數(shù),s就是加密后的參數(shù)。chunk-common.a25fd3ce.js就是進(jìn)行加密的js。通過點(diǎn)擊堆棧的js名稱就可以定位到對(duì)應(yīng)的地方。
扣取代碼
經(jīng)過上一步分析,我們知道o就是加密之前的參數(shù),就在js代碼找o定義的位置,如下圖所示:
下面代碼就是請(qǐng)求體的參數(shù),通過上述代碼發(fā)現(xiàn)id是通過uuid方法生成的。
{
"id": "rtmhwib79r4ytdgn",
"projectKey": "honsan_cloud_ccprec",
"clientKey": "rtmhwialwggc91l6",
"token": null,
"clientDailyData": {},
"acts": [
{
"id": "rtmhwib65mon96h1",
"fullPath": "/cloud.sys.tomcatV11/api/v1/template/getPages",
"args": [
{
"yuPiLou": {
"templateId": "32d3044760964ed8927bad49e545ba4b",
"pageNo": 1,
"pageSize": 20,
"where": {
"projecttype": "PG3",
"state": "42"
}
}
}
]
}
]
}
接著我們需求扣取這個(gè)uuid方法。通過鼠標(biāo)懸停在這個(gè)方法上面,就會(huì)彈出面板,如下圖所示:
點(diǎn)擊后就會(huì)調(diào)到uuid方法定義的地方,如下圖所示:
把這段代碼扣取下面,如下所示:
uuid = function (t, n) {
void 0 === t && (t = 16),
void 0 === n && (n = !1),
!n && t < 16 && (console.error("uuid useCase=false 時(shí) len 不能小于 16"),
t = 16),
n && t < 12 && (console.error("uuid useCase=true 時(shí) len 不能小于 12"),
t = 12);
var i = ((new Date).getTime() + 1e14).toString();
return i += ("000" + (++e.uuidCount).toString()).substr(-3, 3),
i = n ? parseInt(i).to62() : parseInt(i).toString(36),
i += randomStr(t),
i = i.substr(0, t),
i
}
把this關(guān)鍵的全部刪除它。
然后通過node進(jìn)行運(yùn)行,如下圖所示報(bào)錯(cuò)了e沒有定義。
于是,我們?cè)偃ピ创a中找改值是什么?通過選中該值,經(jīng)過幾次的確認(rèn)。該值是NaN,如下圖所示:
我們?cè)僮约嚎廴〉拇a寫死它,再次運(yùn)行。如下圖所示:
發(fā)現(xiàn)這是報(bào)randomStr沒有定義,我們?cè)偃ピ创a找這個(gè)方法進(jìn)行補(bǔ)環(huán)境。通過搜索發(fā)現(xiàn)了randomStr方法,該方法有調(diào)用了random。于是我們兩個(gè)方法一起扣取下來。
再次運(yùn)行,沒有報(bào)錯(cuò)了,成功打印了uuid,如下圖所示:
把之前扣取下來的請(qǐng)求體,id字段固定值替換成uuid函數(shù)生成的動(dòng)態(tài)值,如下圖所示:
最重要的一步就是扣取加密的JS代碼啦, 在請(qǐng)求體參數(shù)附近查看this.aes.encode(a)加密方法,如下圖所示:
通過上面介紹的方面快速定位到這個(gè)加密的方法,如下圖所示:
該方法又調(diào)用了另外一個(gè)方法this.encryptCode,通過關(guān)鍵搜索也找到了encryptCode方法,如下圖所示:
加密方法
按照編程習(xí)慣,附近也會(huì)有對(duì)應(yīng)的解密方法的,如下圖所示:
解密方法
現(xiàn)在加密和解密的方法都找到了,我們把它全部扣取下面進(jìn)行調(diào)試,發(fā)現(xiàn)缺什么方法再進(jìn)行補(bǔ)。重新定義一個(gè)方法生成加密請(qǐng)求頭,后續(xù)讓python調(diào)用這個(gè)方法并傳入分頁參數(shù)。如下圖所示:
測(cè)試該方法是否能正常生成加密請(qǐng)求頭,執(zhí)行如下命令:
console.log(encrypt())
執(zhí)行上述方法后,輸入如下圖所示:
通過python模擬請(qǐng)求
這里介紹一個(gè)非??旖莸姆绞接胮ython模擬一個(gè)瀏覽器請(qǐng)求,首先我們先在開發(fā)者模式選擇這條請(qǐng)求,然后右擊選擇**將所有列表復(fù)制為cURL(Bash)**,如下圖所示:
然后,來的這個(gè)工具網(wǎng)站[1],粘貼剛才復(fù)制的cURL信息,就可以根據(jù)自己喜歡的編程語言生成模擬瀏覽器請(qǐng)求的代碼,如下圖所示:
然后,把生成的代碼拷貝下來,進(jìn)行修改。通過python的第三方庫execjs調(diào)用js代碼并執(zhí)行對(duì)應(yīng)的方法。
jscode=open("./test.js",'r',encoding='utf-8').read()
以讀的模式打開test.js并賦值給jscode
data = execjs.compile(jscode).call("encrypt",page)
通過execjs調(diào)用jscode對(duì)象,并通過call方法調(diào)用encrypt加密方法,并傳入分頁參數(shù)page。
下圖是python中的完整代碼:
執(zhí)行上述代碼后,成功獲取網(wǎng)站的數(shù)據(jù)如下圖所示:
遇到的問題
在python中使用execjs庫,出現(xiàn)UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xac in position 244: illegal multibyte sequence,一般遇見編碼問題先看看代碼里寫沒寫encoding='utf-8',沒寫的話寫加上試試。加了也不行。通過在python代碼中加入如下代碼:
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
腳本獲取方式
上述腳本已經(jīng)上傳上傳到gitee,有需要的小伙伴可以自行獲取。gitee上的倉庫主要是分享一些工作中常用的腳本。小伙伴可以frok或者watch倉庫,這樣有更新可以及時(shí)關(guān)注到。
倉庫地址:https://gitee.com/didiplus/script
工具網(wǎng)站: https://curlconverter.com/