在 - 簡書-爬蟲數(shù)據(jù)分析學習交流 - 微信群里有位朋友Jacky提到爬取中國銀行遇到的問題,一時興起便做了嘗試迈倍。
-
首先還原問題伤靠,我們禁用js,在chrome瀏覽器中新建標簽頁啼染,F(xiàn)12 > F1 >打開設(shè)置在右下角找到禁用js并勾選
- 打開中國人民銀行條法司網(wǎng)頁發(fā)現(xiàn)如下的頁面顯示
-
然后F12關(guān)閉開發(fā)者控制臺宴合,刷新頁面,顯示正常
- 利用chrome插件迹鹅,EditThisCookie卦洽,在控制臺中查看cookie如下,同時禁用js再次打開網(wǎng)頁卻發(fā)現(xiàn)顯示正常斜棚,而清空cookie(禁用js)后打開又出現(xiàn)問題顯示阀蒂。
- 這是因為該網(wǎng)頁首先傳給你的html文件中包含cookie設(shè)置和動態(tài)跳轉(zhuǎn)網(wǎng)址的js代碼该窗,js代碼運行后會自動設(shè)置cookie并跳轉(zhuǎn)鏈接,到達正常頁面蚤霞。
-
我們查看問題頁面的源碼酗失,Ctrl-U
- 源碼很亂,以我的菜雞水平根本看不出來什么東西昧绣,這時需要優(yōu)秀工具的幫助规肴,http://jsbeautifier.org/ 是一款優(yōu)秀js代碼美化和解析工具,我們將代碼放上去解析夜畴,終端curl網(wǎng)頁得到的js代碼和解析后的代碼對比鮮明
-
與一般的代碼美化工具比較(下圖)奏纪,不僅格式化了代碼,并且可讀化了代碼斩启,這樣以我的水平就可以分析代碼了序调。
-
首先兩次請求該網(wǎng)址,將兩次美化后的代碼進行對比兔簇,我們可以看到不僅在js全局變量上有改變发绢,在其中一個加密函數(shù)里也有小改動。
- 某一個請求里的js代碼垄琐,留存參考边酒,閱讀可先跳過:
var dynamicurl = "/L3RpYW9mYXNpLzE0NDk0MS8xNDQ5NTcvaW5kZXguaHRtbA==";
var wzwschallenge = "RANDOMSTR14925";
var wzwschallengex = "STRRANDOM14925";
var template = 4;
var encoderchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function KTKY2RBD9NHPBCIHV9ZMEQQDARSLVFDU(str) {
var out, i, len;
var c1, c2, c3;
len = str.length;
i = 0;
out = "";
while (i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if (i == len) {
out += encoderchars.charAt(c1 >> 2);
out += encoderchars.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if (i == len) {
out += encoderchars.charAt(c1 >> 2);
out += encoderchars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
out += encoderchars.charAt((c2 & 0xf) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += encoderchars.charAt(c1 >> 2);
out += encoderchars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
out += encoderchars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6));
out += encoderchars.charAt(c3 & 0x3f);
}
return out;
}
function findDimensions() {
var w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
var h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
if (w * h <= 120000) {
return true;
}
var x = window.screenX;
var y = window.screenY;
if (x + w <= 0 || y + h <= 0 || x >= window.screen.width || y >= window.screen.height) {
return true;
}
return false;
}
function QWERTASDFGXYSF() {
var tmp = wzwschallenge + wzwschallengex;
var hash = 0;
var i = 0;
for (i = 0; i < tmp.length; i++) {
hash += tmp.charCodeAt(i);
}
hash *= 11;
hash += 111111;
return "WZWS_CONFIRM_PREFIX_LABEL4" + hash;
}
function HXXTTKKLLPPP5() {
if (findDimensions()) {} else {
var cookieString = "";
cookieString = "wzwstemplate=" + KTKY2RBD9NHPBCIHV9ZMEQQDARSLVFDU(template.toString()) + "; path=/";
document.cookie = cookieString;
var confirm = QWERTASDFGXYSF();
cookieString = "wzwschallenge=" + KTKY2RBD9NHPBCIHV9ZMEQQDARSLVFDU(confirm.toString()) + "; path=/";
document.cookie = cookieString;
window.location = dynamicurl;
}
}
HXXTTKKLLPPP5();
- 這種程度的js代碼(美化后)并不難理解,很明顯可以看出最后的函數(shù)調(diào)用中利用document.cookie設(shè)置了兩個cookie狸窘,并利用window.location設(shè)置了跳轉(zhuǎn)網(wǎng)址墩朦,靜下心去分析也能用python寫出相應的加密程序,用正則取配到變量翻擒,生成我們想要的信息氓涣,但是有沒有更快的方法呢。這時我們又有一個強大的工具陋气,Js2Py劳吠,利用它我們可以解析js代碼變?yōu)閜ython中的可執(zhí)行代碼。如下官方簡單示例:
- 下面的思路就清晰了巩趁,先利用js美化工具的python庫jsbeautifier(pip安裝)美化代碼痒玩,然后不管js代碼中關(guān)于dom操作的內(nèi)容(如window.xx),取出js全局變量和加密函數(shù)议慰,利用js2py生成可執(zhí)行的python函數(shù)蠢古,以js代碼中的主干邏輯在python里執(zhí)行獲得cookies,在requests.session中更新cookies并訪問js變量中的動態(tài)網(wǎng)址别凹,就可以成功到達內(nèi)容頁草讶,開始爬取解析了。
- 以下為python代碼番川,可以看到最后成功驗證我們爬取到了有效信息到涂。
import requests
import re
import jsbeautifier
import js2py
host_url = 'http://www.pbc.gov.cn/'
dest_url = 'http://www.pbc.gov.cn/tiaofasi/144941/144957/index.html'
# 利用session保存cookie信息,第一次請求會設(shè)置cookie類似{'wzwsconfirm': 'ab3039756ba3ee041f7e68f634d28882', 'wzwsvtime': '1488938461'}颁督,與js解析得到的cookie合起來才能通過驗證
r = requests.session()
content = r.get(dest_url).content
# 獲取頁面腳本內(nèi)容
re_script = re.search(r'<script type="text/javascript">(?P<script>.*)</script>', content.decode('utf-8'), flags=re.DOTALL)
# 用點匹配所有字符践啄,用(?P<name>...)獲取:https://docs.python.org/3/howto/regex.html#regex-howto
# cheatsheet:https://github.com/tartley/python-regex-cheatsheet/blob/master/cheatsheet.rst
script = re_script.group('script')
script = script.replace('\r\n', '')
# 在美化之前沉御,去掉\r\n之類的字符才有更好的效果
res = jsbeautifier.beautify(script)
# 美化并一定程度解析js代碼:https://github.com/beautify-web/js-beautify
with open('x.js','w') as f:
f.write(res)
# 寫入文檔進行查看分析
jscode_list = res.split('function')
var_ = jscode_list[0]
var_list = var_.split('\n')
template_js = var_list[3] # 依順序獲取屿讽,亦可用正則
template_py = js2py.eval_js(template_js)
# 將所有全局變量插入第一個函數(shù)變?yōu)榫植孔兞坎⒂嬎?function1_js = 'function' + jscode_list[1]
position = function1_js.index('{') +1
function1_js = function1_js[:position]+ var_ +function1_js[position:]
function1_py = js2py.eval_js(function1_js)
cookie1 = function1_py(str(template_py)) # 結(jié)果類似'NA=='
# 保存得到的第一個cookie
cookies = {}
cookies['wzwstemplate'] = cookie1
# 對第三個函數(shù)做類似操作
function3_js = 'function' + jscode_list[3]
position = function3_js.index('{') +1
function3_js = function3_js[:position]+ var_ +function3_js[position:]
function3_py = js2py.eval_js(function3_js)
middle_var = function3_py() # 是一個str變量,結(jié)果類似'WZWS_CONFIRM_PREFIX_LABEL4132209'
cookie2 = function1_py(middle_var)
cookies['wzwschallenge'] = cookie2
# 關(guān)于js代碼中的document.cookie參見 https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
dynamicurl = js2py.eval_js(var_list[0])
# 利用新的cookie對提供的動態(tài)網(wǎng)址進行訪問即是我們要達到的內(nèi)容頁面了
r.cookies.update(cookies)
content = r.get(host_url+dynamicurl).content
# 最后驗證是否爬取到有效信息
if u'銀行卡清算機構(gòu)管理辦法' in content.decode('utf-8'):
print('success')