應(yīng)對(duì)js反爬蟲的嘗試,爬取中國人民銀行

在 - 簡書-爬蟲數(shù)據(jù)分析學(xué)習(xí)交流 - 微信群里有位朋友Jacky提到爬取中國銀行遇到的問題,一時(shí)興起便做了嘗試。


  • 首先還原問題,我們禁用js,在chrome瀏覽器中新建標(biāo)簽頁,F(xiàn)12 > F1 >打開設(shè)置在右下角找到禁用js并勾選


  • 打開中國人民銀行條法司網(wǎng)頁發(fā)現(xiàn)如下的頁面顯示
  • 然后F12關(guān)閉開發(fā)者控制臺(tái),刷新頁面,顯示正常


  • 利用chrome插件,EditThisCookie,在控制臺(tái)中查看cookie如下,同時(shí)禁用js再次打開網(wǎng)頁卻發(fā)現(xiàn)顯示正常,而清空cookie(禁用js)后打開又出現(xiàn)問題顯示。
  • 這是因?yàn)樵摼W(wǎng)頁首先傳給你的html文件中包含cookie設(shè)置和動(dòng)態(tài)跳轉(zhuǎn)網(wǎng)址的js代碼,js代碼運(yùn)行后會(huì)自動(dòng)設(shè)置cookie并跳轉(zhuǎn)鏈接,到達(dá)正常頁面。
  • 我們查看問題頁面的源碼,Ctrl-U


    Paste_Image.png
  • 源碼很亂,以我的菜雞水平根本看不出來什么東西,這時(shí)需要優(yōu)秀工具的幫助,http://jsbeautifier.org/ 是一款優(yōu)秀js代碼美化和解析工具,我們將代碼放上去解析,終端curl網(wǎng)頁得到的js代碼和解析后的代碼對(duì)比鮮明
  • 與一般的代碼美化工具比較(下圖),不僅格式化了代碼,并且可讀化了代碼,這樣以我的水平就可以分析代碼了。


  • 首先兩次請(qǐng)求該網(wǎng)址,將兩次美化后的代碼進(jìn)行對(duì)比,我們可以看到不僅在js全局變量上有改變,在其中一個(gè)加密函數(shù)里也有小改動(dòng)。



  • 某一個(gè)請(qǐng)求里的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è)置了兩個(gè)cookie,并利用window.location設(shè)置了跳轉(zhuǎn)網(wǎng)址,靜下心去分析也能用python寫出相應(yīng)的加密程序,用正則取配到變量,生成我們想要的信息,但是有沒有更快的方法呢。這時(shí)我們又有一個(gè)強(qiáng)大的工具,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變量中的動(dòng)態(tài)網(wǎng)址,就可以成功到達(dá)內(nèi)容頁,開始爬取解析了。
  • 以下為python代碼,可以看到最后成功驗(yàn)證我們爬取到了有效信息。
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信息,第一次請(qǐng)求會(huì)設(shè)置cookie類似{'wzwsconfirm': 'ab3039756ba3ee041f7e68f634d28882', 'wzwsvtime': '1488938461'},與js解析得到的cookie合起來才能通過驗(yàn)證
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)
# 用點(diǎn)匹配所有字符,用(?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)
# 寫入文檔進(jìn)行查看分析

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)
# 將所有全局變量插入第一個(gè)函數(shù)變?yōu)榫植孔兞坎⒂?jì)算
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=='
# 保存得到的第一個(gè)cookie
cookies = {}
cookies['wzwstemplate'] = cookie1
# 對(duì)第三個(gè)函數(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() # 是一個(gè)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對(duì)提供的動(dòng)態(tài)網(wǎng)址進(jìn)行訪問即是我們要達(dá)到的內(nèi)容頁面了
r.cookies.update(cookies)
content = r.get(host_url+dynamicurl).content

# 最后驗(yàn)證是否爬取到有效信息
if u'銀行卡清算機(jī)構(gòu)管理辦法' in content.decode('utf-8'):
    print('success')
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容