現(xiàn)在的網(wǎng)頁代碼搞得越來越復(fù)雜,除了使用vue等前端框架讓開發(fā)變得容易外,主要就是為了防爬蟲,所以寫爬蟲下的功夫就越來越多。攻和防在互相廝殺中結(jié)下孽緣卻又相互提升著彼此。
本文就JS反爬蟲的策略展開討論,看看這中間都有著怎樣的方法破解。
一 、JS寫cookie
我們要寫爬蟲抓某個網(wǎng)頁里面的數(shù)據(jù),無非是打開網(wǎng)頁,看看源代碼,如果html里面有我們要的數(shù)據(jù),那就簡單了。用requests請求網(wǎng)址得到網(wǎng)頁源代碼然后解析提取。
等等!requests得到的網(wǎng)頁是一對JS,跟瀏覽器打開看到的網(wǎng)頁源碼完全不一樣!這種情況,往往是瀏覽器運(yùn)行這段JS生成一個(或多個)cookie再帶著這個cookie做二次請求。服務(wù)器那邊收到這個cookie就認(rèn)為你的訪問是通過瀏覽器過來的合法訪問。
其實,你在瀏覽器(chrome、Firefox都可以)里可以看到這一過程。首先把Chrome瀏覽器保存的該網(wǎng)站的cookie刪除,按F12到Network窗口,把“preserve log”選中(Firefox是“Persist logs”),刷新網(wǎng)頁,這樣我們就可以看到歷史的Network請求記錄。比如下圖:
第一次打開“index.html”頁面時返回的是521, 內(nèi)容是一段JS代碼;第二次請求這個頁面就得到了正常的HTML。查看兩次請求的cookies,可以發(fā)現(xiàn)第二次請求時帶上了一個cookie,而這個cookie并不是第一次請求時服務(wù)器發(fā)過來的。其實它就是JS生成的。
對策就是,研究那段JS,找到它生成cookie的算法,爬蟲就可以解決這個問題。
二、JS加密ajax請求參數(shù)
寫爬蟲抓某個網(wǎng)頁里面的數(shù)據(jù),發(fā)現(xiàn)網(wǎng)頁源代碼里面沒有我們要的數(shù)據(jù),那就有點麻煩了。那些數(shù)據(jù)往往是ajax請求得到的。但是也不用怕,按F12打開Network窗口,刷新網(wǎng)頁看看加載這個網(wǎng)頁都下載了哪些URL,我們要的數(shù)據(jù)就在某個URL請求的結(jié)果里面。這類URL在Chrome的Network里面的類型大多是XHR。通過觀察它們的“Response”就可以發(fā)現(xiàn)我們要的數(shù)據(jù)。
然而事情往往不是這么順利,這個URL包含很多參數(shù),某個參數(shù)是一串看上去無意義的字符串。這個字符串很可能是JS通過一個加密算法得到的,服務(wù)器也會通過同樣的算法進(jìn)行驗證,驗證通過了才認(rèn)為你這是從瀏覽器來的請求。我們可以把這個URL拷貝到地址欄,把那個參數(shù)隨便改個字母,訪問一下看看是不是能得到正確的結(jié)果,由此來驗證它是否是很重要的加密參數(shù)。
對于這樣的加密參數(shù),對策是通過debug JS來找到對應(yīng)的JS加密算法。其中關(guān)鍵的是在Chrome里面設(shè)置“XHR/fetch Breakpoints”。
三、JS反調(diào)試(反debug)
前面我們都用到了Chrome 的F12去查看網(wǎng)頁加載的過程,或者是調(diào)試JS的運(yùn)行過程。這種方法用多了,網(wǎng)站就加了反調(diào)試的策略,只有我們打開F12,就會暫停在一個“debugger”代碼行,無論怎樣都跳不出去。它看起來像下面這樣:
不管我們點擊多少次繼續(xù)運(yùn)行,它一直在這個“debugger”這里,每次都會多出一個VMxx的標(biāo)簽,觀察“Call Stack”發(fā)現(xiàn)它好像陷入了一個函數(shù)的遞歸調(diào)用。這個“debugger”讓我們無法調(diào)試JS。但是關(guān)掉F12窗口,網(wǎng)頁就正常加載了。
解決這種JS反調(diào)試的方法我們稱之為“反-反調(diào)試”,其策略是:通過“Call Stack”找到把我們帶入死循環(huán)的函數(shù),重新定義它。
這樣的函數(shù)幾乎沒有任何其它功能只是給我們設(shè)置的陷阱。我們可以把這個函數(shù)在“Console”里面重新定義,比如把它重新定義為空函數(shù),這樣再運(yùn)行它時就什么都不做,也就不會把我們帶人陷阱。在這個函數(shù)調(diào)用的地方打個“Breakpoint”。因為我們已經(jīng)在陷阱里面了,所以要刷新頁面,JS的運(yùn)行應(yīng)該停止在設(shè)置的斷點處,此時該函數(shù)尚未運(yùn)行,我們在Console里面重新定義它,繼續(xù)運(yùn)行就可以跳過該陷阱。
四、JS發(fā)送鼠標(biāo)點擊事件
還有些網(wǎng)站,它的反爬都不是上面的方式。你從瀏覽器可以打開正常的頁面,而在requests里面卻被要求輸入驗證碼或重定向其它網(wǎng)頁。起初你可能一頭霧水,但不要怕,認(rèn)真看看“Network”或許能發(fā)現(xiàn)些線索。比如下面這個Network流里面的信息:
認(rèn)真觀察后發(fā)現(xiàn),每點擊頁面的的鏈接,它都會做一個“cl.gif”的請求,它看上去是下載一個gif圖片,然而并不是。它請求時發(fā)送的參數(shù)非常多,而且這些參數(shù)都是當(dāng)前頁面的信息。比如包含了被點擊的鏈接等等。
我們先來梳理一下它的邏輯。JS會響應(yīng)鏈接被點擊的事件,在打開鏈接前,先訪問cl.gif,把當(dāng)前的信息發(fā)送給服務(wù)器,然后再打開被點擊的鏈接。服務(wù)器收到被點擊鏈接的請求,會看看之前是不是已經(jīng)通過cl.gif把對應(yīng)信息發(fā)過來,如果發(fā)過來了就認(rèn)為是合法的瀏覽器訪問,給出正常的網(wǎng)頁內(nèi)容。
因為requests沒有鼠標(biāo)事件響應(yīng)就沒有訪問cl.gif的過程就直接訪問鏈接,服務(wù)器就拒絕服務(wù)。
明白了這個過程,我們不難拿出對策,幾乎不需要研究JS內(nèi)容(JS也有可能對被點擊鏈接進(jìn)行修改哦)就可以繞過這個反爬策略,無非是在訪問鏈接前先訪問一下cl.gif即可。關(guān)鍵是要研究cl.gif后的參數(shù),把這些參數(shù)都帶上就萬事大吉啦。
結(jié)尾
爬蟲和網(wǎng)站是一對冤家,相克相生。爬蟲知道了反爬策略就可以做成響應(yīng)的反-反爬策略;網(wǎng)站知道了爬蟲的反-反爬策略就可以做一個“反-反-反爬”策略……道高一尺魔高一丈,兩者的斗爭也不會結(jié)束。






