爬蟲js逆向系列
我會把做爬蟲過程中,遇到的所有js逆向的問題分類展示出來,以現象,解決思路,以及代碼實現,這三方面解析,供大家參考
爬蟲認知
在程序猿所有的方向中,爬蟲是離money最近的一個方向,你的明白?而且爬蟲可發展的方向很多,前可走大數據,人工智慧,後可轉後端。而且爬蟲做得好,要求的技術棧還是比較全面的。如果你對爬蟲有興趣,歡迎加V:13809090874,一起溝通交流
免責申明:
此內容僅供學習交流使用,不用於商業用途,如果涉及侵權,聯繫作者刪除
請求頭內容如下:
POST /api/tiktok/ranking/tiktok_goods_sales_rank?ts=1602072436550&he=wqdvbhcTvwD2hXibw4L5RyPHeCsEZ8f7wrD2w4Sh&sign=5e1501c104122454 HTTP/1.1
Host: api.douchacha.com
Connection: keep-alive
Content-Length: 258
dcc-href: https://www.douchacha.com/cable
d-v: NCxaZGJRd3BDVFBzZmlaa1Z4WkhibXdvUUtVc2ZldzZsZ2hkZkR3b09UdWpWeGVPUk13NzlUd3F2VG54SHJLaFlVTkhibUU4YkVHa3BsdzYlMkZVcDhmYVNzZkN3NjhWajhmMkhpY1ROUThNZGtEd1prclV2MlEyVmlNJTJGQ2RiZXc1WkpSMzNVdVNjVHJIZkt3NVlUbzhiYlprZDVaaENVclZ3TWRrVk13cktVb3NiMFprUE13NXAlMkJ3cmNVdmglM0QlM0Q=
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoiUEMiLCJleHAiOjE2MDI2Njg2NDksInVzZXJJZCI6MTI5OTIyNjYzOTc0OTc1MDc4NCwiY3JlYXRlRGF0ZSI6IjIwMjAtMTAtMDcgMTc6NDQ6MDkifQ.ppW18NruEU9gpgRJgbYIaEUkvD7cTmfcRBXgOQ8vamE
Content-Type: application/json;charset=UTF-8
Accept: application/json, text/plain, */*
s-id: 371
dcc-r: https://www.douchacha.com/cable
d-t: 1602072436550
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
j-id: sem
Origin: https://www.douchacha.com
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://www.douchacha.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9url中有兩個加密參數he與ts,ts看著就像是時間戳(毫秒級)
請求頭中有幾個加密參數:d-v,Authorization,d-t
大家有沒有發現一個問題,就是這個網站沒有用cookies。對的,它沒有用cookies做身份校驗,而是用Authorization來做用戶校驗,所以我們必須要找到Authorization的加密方式。
2.尋找加密參數封裝的地方對於去尋找目標網站的加密參數,有幾個思路跟大家分享一下:
直接搜關鍵詞,我們要找he,d-v這樣的加密參數,直接在js中搜,看是否能搜到,這也是最常用,最直接的辦法
對於一些完全混淆過的js,直接搜是無法搜到的,那麼就看它的調用棧,一層層斷點打進去。
對於我要爬的網站,還算友好,我直接搜he,d-v就搜到了相對應的代碼部分,如下圖
從這段代碼中,我們可以發現所有請求頭的加密參數都是在這裡封裝的。第6489行代碼:t.url = "".concat(t.url, "?ts=").concat(e, "&he=").concat(s, "&sign=").concat(c);
1這是url的封裝參數,ts與he,ts=e,he=s,sign=c
return i && (t.headers.common["Authorization"] = i),
t.headers.common["d-t"] = e,
u && (t.headers.common["j-id"] = u),
d && (t.headers.common["s-id"] = d),
g && (t.headers.common["dcc-href"] = g),
t.headers.common["dcc-r"] = document.referrer || "",
t這是請求頭的封裝參數。
找到這裡後,我們就要去找這些值是怎麼生成的。
ts:e = (new Date).getTime() + 1 * sessionStorage.getItem("diffDate") || 0
1ts就是當前時間戳+session裡一個時間差,這個時間差通過閱讀源碼可以發現,這是頁面加載的時間,可以自己隨機生成一下
he:i = JSON.parse(localStorage.getItem("token"))
s = window.he(i ? "uid" : "dt")
12判斷localStorage中的token是否存在,存在就調用window.he(uid),不存在就調用window.he(「dt」),window對象的原生方法中沒有he這個方法,那麼he()就是他們自定義的方法,我們後面著重去找這個方法就可以了。
sign:e = (new Date).getTime() + 1 * sessionStorage.getItem("diffDate") || 0
n = t.url.split("https://api.douchacha.com")[1];
o = n + e
c = window.sh(o)
1234從這裡也可以看出sign值加密的方式,主要是找sh生成方法。o參數,我們可以模擬出來。
d-v:l = window.btoa(window.v() + "," + window.hi("dt"));
t.headers.common["d-v"] = l
12d-v這個參數就是主要找v()和hi()這兩個方法的聲明。
Authorization:i = localStorage.getItem("token")
return i && (t.headers.common["Authorization"] = i),
12token就是登錄時的帳號驗證信息
3.繼續潛入,找到加密方法生成方法我們之前說過這個網站的主要加密地方為app.js和s.js。通過搜索方法名,我們可以很快的找到這些方法聲明的地方
我們搜到了這個方法的地方,但是這裡都是十六進位的加密,破解起來非常麻煩。這個js文件還好,只有400多行,把它完全破解,也還好,沒那麼費力。但是要是上萬行,幾萬行,那就要了老命了。
我們可以通過hook的方式,黑盒的方式來解決這個問題,沒必要搞定它是怎麼加密的。把這個js下載下來,利用python的execjs這個包去直接調用js函數。就可以解決問題。
注意:運行execjs包的時候,需要先安裝nodejs,不然會沒有js的運行環境
execjs包的安裝:pip install PyExecJs
1python代碼:
import execjs
js = execjs.compile(open(r"s_my_press.js").read())
url = 'https://api.douchacha.com/api/tiktok/ranking/user_list_gain'
diffdate = 0
n = str(int(round(time.time()*1000)) + diffdate)
# n = '1599562900780'
print('n:'+str(n))
e = url.split('https://api.douchacha.com')[1]
o = e+str(n)
r = js.call('he','1299226639749750784')
r = encode_b64_url(r)
print('he',r)
s = js.call('sh',o)
url = url+'?ts={}&he={}&sign={}'.format(n,r,s)
hi = encode_b64_url(js.call('hi',n))
print('hi:'+hi)
d_v = base64.b64encode((str(4) + ',' + hi).encode())
print('d_v'+str(d_v))
headers ={
'Host': 'api.douchacha.com',
'Connection': 'keep-alive',
'Content-Length': '101',
'dcc-href': 'https://www.douchacha.com/uppoint',
'd-v': d_v,
'Authorization': "eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoiUEMiLCJleHAiOjE1OTk4MjA1NjQsInVzZXJJZCI6MTI5OTIyNjYzOTc0OTc1MDc4NCwiY3JlYXRlRGF0ZSI6IjIwMjAtMDktMDQgMTg6MzY6MDQifQ.KCrYYx4hEqzv6CTJw2NlvD8pp-iRMw7IBgud_XwHHRE",
'Content-Type': 'application/json;charset=UTF-8',
'Accept': 'application/json, text/plain, */*',
's-id': '371',
'dcc-r': 'https://www.douchacha.com/uppoint',
'd-t': str(n),
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36',
'j-id': 'sem',
'Origin': 'https://www.douchacha.com',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://www.douchacha.com/uppoint',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9'
}
data = {"page_no":1,"page_size":20,"params_data":{"label_name":"","period":"DAY","period_value":"20200907"}}
r1 = sess.post(url,json=data, headers=headers, verify=False)
print(r1.text)
1234567891011121314151617181920212223242526272829303132333435363738394041424344這樣就可以成功的抓取到數據了。
當然這裡面會有一些小坑。js中的window對象在execjs中運行會報錯,因為execjs沒有瀏覽器這個對象,所以我們必須要把window對象裡面的方法拎出來,用python去執行,比如window.btoa這是js中base64加密方法,必須用python的base64去重新生成
js中默認的編碼方式iso-8859-1,而python中不是,在調用python中base64加密時得要表明編碼方式為 iso-8859-1
至此,我們前期破解的工作已經完成。至於該網站的登錄問題,比較簡單,就不寫了。也很容易拿到token。後續會更新的,記得給我關注哦!
完整項目原始碼獲取加下交流群: 1136192749