從零實現(xiàn)一個高性能網(wǎng)絡(luò)爬蟲(二)應(yīng)對反爬蟲之前端數(shù)據(jù)混淆

摘要

  • 上一篇以知乎網(wǎng)為例簡單分享網(wǎng)絡(luò)請求分析忍啸。這一篇主要分享一種應(yīng)對反爬蟲的方法,前端數(shù)據(jù)混淆狮荔。

目的

  • 之前寫https://github.com/wycm/zhihu-crawler項目的時候架专,需要用到免費的http代理,然后找到了這個 http://www.goubanjia.com/ 這個網(wǎng)站⌒庵粒現(xiàn)在需要把這個網(wǎng)站上的ip和port爬取下來,有興趣的朋友也可以嘗試自己爬取一下译秦。

開始

  • 打開這個網(wǎng)站首頁峡捡,然后控制臺查看ip和port的對應(yīng)標(biāo)簽。


  • 如上圖(圖一)筑悴,從控制臺的標(biāo)簽中可以看出ip加了一些無關(guān)不顯示的標(biāo)簽來混淆數(shù)據(jù)们拙,這里混淆的原理其實很簡單,通過標(biāo)簽的style="display:none"屬性來達到混淆的目的阁吝,也就是包含這個屬性的標(biāo)簽是不會顯示在頁面上的砚婆。知道了這一點就比較好處理了,只需要在解析的時候把包含style="display:none"屬性的標(biāo)簽去掉求摇。就可以輕松的拿到ip和port數(shù)據(jù)了射沟。
  • 代碼如下
package com.cnblogs.wycm;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.net.URL;

/**
 *
 * 數(shù)據(jù)的解析采用的是Jsoup框架,Jsoup是一個操作HTML標(biāo)簽的Java庫与境,它提供了非常方便的API來提取和操縱庫验夯,支持類似jquery的選擇器來查找標(biāo)簽。
 * 由于請求比較單一摔刁,這里的網(wǎng)絡(luò)請求并沒有采用上一篇所使用HttpClient框架挥转。直接通過Jsoup來執(zhí)行http請求的。
 * 關(guān)于Jsoup的使用可以參考http://www.open-open.com/jsoup/
 *
 */
public class Chapter1 {
    public static void main(String[] args) throws IOException {
        Document document= Jsoup.parse(new URL("http://www.goubanjia.com/"), 10000);
        //獲取class='table'的table的所有子節(jié)點tr
        Elements elements = document.select("table[class=table] tr");
        for (int i = 1; i < elements.size(); i++){
            //獲取td節(jié)點
            Element td = elements.get(i).select("td").first();
            /**
             * 查找所有style屬性包含none字符串的標(biāo)簽(頁面上未顯示的標(biāo)簽)共屈,并移除
             * 包括以下兩種
             * style=display: none;
             * style=display:none;
             */
            for(Element none : td.select("[style*=none;]")){
                none.remove();
            }
            //移除空格
            String ipPort = td.text().replaceAll(" ", "");
            //打印
            System.out.println(ipPort);
        }
    }
}
/*
第一次運行打印結(jié)果:
183.129.246.228:8132
222.92.136.206:8987
54.238.186.100:8988
...
第二次運行打印結(jié)果:
183.129.246.228:8377
222.92.136.206:9059
54.238.186.100:8622
...
*/
  • ip地址能夠準(zhǔn)確的拿到了绑谣,卻發(fā)現(xiàn)port被做了混淆,而且每次返回的port還在動態(tài)改變拗引。大家可以通過把瀏覽器的JavaScrip腳本關(guān)閉后借宵,然后刷新這個網(wǎng)頁。會發(fā)現(xiàn)每次的port都不一樣矾削。我們每次看到的正確port都是通過JavaScript腳本處理后的壤玫。如果采用普通爬蟲的方式拿到的port都是錯誤的。現(xiàn)在要想拿到正確的port哼凯,可以通過分析它JavaScrip腳本還原數(shù)據(jù)的邏輯欲间。
  • 同樣打開控制臺->選擇Sources->選擇一行js代碼打斷點(點擊行編號),如下圖


  • 刷新網(wǎng)頁—>頁面Paused in debugger—>選擇Elements->右鍵td節(jié)點->Break on...->subtree modifications断部。這兩個步驟就是在設(shè)置斷點調(diào)試猎贴,也就是在td節(jié)點發(fā)生改變的時候paused。


  • 選擇Sources->F8(繼續(xù)執(zhí)行),這個時候又會有一次pause她渴,也就是js腳本在還原正確port的時候(如下圖)


  • 函數(shù)的調(diào)用棧有好多層达址,如何快速定位哪一個函數(shù)的技巧就是,看它局部變量表的變量變化惹骂,因為這里是port在發(fā)生改變苏携,然后找到對應(yīng)變量和對應(yīng)邏輯函數(shù)。簡單分析可以確定到port發(fā)生改變的函數(shù)是一個匿名函數(shù)对粪,如下圖


  • 格式化后,代碼如下:
var _$ = ['\x2e\x70\x6f\x72\x74', "\x65\x61\x63\x68", "\x68\x74\x6d\x6c", "\x69\x6e\x64\x65\x78\x4f\x66", '\x2a', "\x61\x74\x74\x72", '\x63\x6c\x61\x73\x73', "\x73\x70\x6c\x69\x74", "\x20", "", "\x6c\x65\x6e\x67\x74\x68", "\x70\x75\x73\x68", '\x41\x42\x43\x44\x45\x46\x47\x48\x49\x5a', "\x70\x61\x72\x73\x65\x49\x6e\x74", "\x6a\x6f\x69\x6e", ''];
$(function() {
    $(_$[0])[_$[1]](function() {
        var a = $(this)[_$[2]]();
        if (a[_$[3]](_$[4]) != -0x1) {
            return
        }
        ;var b = $(this)[_$[5]](_$[6]);
        try {
            b = (b[_$[7]](_$[8]))[0x1];
            var c = b[_$[7]](_$[9]);
            var d = c[_$[10]];
            var f = [];
            for (var g = 0x0; g < d; g++) {
                f[_$[11]](_$[12][_$[3]](c[g]))
            }
            ;$(this)[_$[2]](window[_$[13]](f[_$[14]](_$[15])) >> 0x3)
        } catch (e) {}
    })
})
  • 還原后如下:
var _$ = ['.port', "each", "html", "indexOf", '*', "attr", 'class', "split", " ", "", "length", "push", 'ABCDEFGHIZ', "parseInt", "join", ''];
$(function() {
    $('.port').each(function() {
        var a = $(this).html();
        if (a.indexOf('*') != -0x1) {
            return
        }
        ;var b = $(this).attr('class');
        try {
            b = (b.split(" "))[0x1];
            var c = b.split("");
            var d = c.length;
            var f = [];
            for (var g = 0x0; g < d; g++) {
                f.push('ABCDEFGHIZ'.indexOf(c[g]))
            }
            ;$(this).html(window.parseInt(f.join('')) >> 0x3)
        } catch (e) {}
    })
})
  • 這段代碼的邏輯装蓬,獲取port標(biāo)簽的class屬性值著拭,取出屬性中后面的幾個大寫字母,遍歷該字符串牍帚,找出每次字符在'ABCDEFGHIZ'這個字符串中的索引儡遮,然后parseInt轉(zhuǎn)換為整數(shù),然后進行右移3位的操作暗赶。
  • 完整代碼實現(xiàn)
package com.cnblogs.wycm;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.URL;

public class Chapter2 {
    public static void main(String[] args) throws IOException {
        Document document= Jsoup.parse(new URL("http://www.goubanjia.com/"), 10000);
        setPort(document);
        //獲取class='table'的table的所有子節(jié)點tr
        Elements elements = document.select("table[class=table] tr");
        for (int i = 1; i < elements.size(); i++){
            //獲取td節(jié)點
            Element td = elements.get(i).select("td").first();
            /**
             * 查找所有style屬性包含none字符串的標(biāo)簽(頁面上未顯示的標(biāo)簽)鄙币,并移除
             * 包括以下兩種
             * style=display: none;
             * style=display:none;
             */
            for(Element none : td.select("[style*=none;]")){
                none.remove();
            }
            //移除空格
            String ipPort = td.text().replaceAll(" ", "");
            //打印
            System.out.println(ipPort);
        }
    }

    /**
     * js代碼port還原
     * @param doc
     */
    private static void setPort(Document doc){
        for (Element e : doc.select(".port")){//$('.port').each(function() {
            String a = e.text();//var a = $(this).html();
            if(a.indexOf("*") != -0x1){//if (a.indexOf('*') != -0x1) {
                return;
            }
            String b = e.attr("class");//var b = $(this).attr('class');
            b = b.split(" ")[0x1];//b = (b.split(" "))[0x1];
            String[] c = b.split("");//var c = b.split("");
            int d = b.length();//var d = c.length;
            StringBuilder f = new StringBuilder();//var f = [];
            for(int g = 0x0; g < d; g++){//for (var g = 0x0; g < d; g++) {
                f.append("ABCDEFGHIZ".indexOf(c[g]));//f.push('ABCDEFGHIZ'.indexOf(c[g]))
            }
            e.text(String.valueOf(Integer.valueOf(f.toString()) >> 0x3));//$(this).html(window.parseInt(f.join('')) >> 0x3)
        }
    }
}
  • maven依賴
 <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.10.2</version>
 </dependency>

總結(jié)

  • 該篇文章簡單分項了下如何應(yīng)對前端混淆的反爬蟲。關(guān)于這種反爬蟲蹂随,還有其它的一些應(yīng)對方式十嘿。如采用無頭瀏覽器的方式,比如phantomjs框架岳锁。這種無頭瀏覽器原本是用來做自動化測試的绩衷。它是基于webkit內(nèi)核的,所以它可以較容易的爬取這種前端混淆的這種網(wǎng)站激率。一般來說瀏覽器能夠正常訪問到的數(shù)據(jù)咳燕,這種方式也可以比較容易爬取這些數(shù)據(jù)。當(dāng)然這種方式的最大問題就是效率比較低乒躺。因為這種方式它每加載一個頁面招盲,都需要下載它的附加資源,如js腳本嘉冒,腳本下載完成后曹货,還要去執(zhí)行js腳本。
  • 我這里采用的方式是閱讀js代碼健爬,得出前端混淆的邏輯控乾,然后再通過目標(biāo)語言來實現(xiàn)對應(yīng)邏輯。這種方式如果針對一些簡單的加密混淆還是很有用的娜遵。但是當(dāng)遇到一些大型復(fù)雜的網(wǎng)站蜕衡,如百度、微博等,需要抓取登錄后的數(shù)據(jù)慨仿。這時候需要來手動模擬登錄久脯,相對來說,這種網(wǎng)站的模擬登錄會更復(fù)雜镰吆,找各種登錄參數(shù)來源帘撰。都會耗費大量精力。分析請求的成本會比較高万皿。這種方式的優(yōu)點就是爬取速度快摧找,只獲取目標(biāo)數(shù)據(jù)。不需要額外網(wǎng)絡(luò)請求成本牢硅。

版權(quán)聲明
作者:wycm
出處:http://www.reibang.com/p/6dc42d309e5a
您的支持是對博主最大的鼓勵蹬耘,感謝您的認真閱讀。
本文版權(quán)歸作者所有减余,歡迎轉(zhuǎn)載综苔,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接位岔,否則保留追究法律責(zé)任的權(quán)利如筛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抒抬,隨后出現(xiàn)的幾起案子杨刨,更是在濱河造成了極大的恐慌,老刑警劉巖瞧剖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拭嫁,死亡現(xiàn)場離奇詭異,居然都是意外死亡抓于,警方通過查閱死者的電腦和手機做粤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捉撮,“玉大人怕品,你說我怎么就攤上這事〗碓猓” “怎么了肉康?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長灼舍。 經(jīng)常有香客問我吼和,道長,這世上最難降的妖魔是什么骑素? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任炫乓,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘末捣。我一直安慰自己侠姑,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布箩做。 她就那樣靜靜地躺著莽红,像睡著了一般。 火紅的嫁衣襯著肌膚如雪邦邦。 梳的紋絲不亂的頭發(fā)上安吁,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音圃酵,去河邊找鬼柳畔。 笑死,一個胖子當(dāng)著我的面吹牛郭赐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播确沸,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捌锭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了罗捎?” 一聲冷哼從身側(cè)響起观谦,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桨菜,沒想到半個月后豁状,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡倒得,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年泻红,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霞掺。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡谊路,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菩彬,到底是詐尸還是另有隱情缠劝,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布骗灶,位于F島的核電站惨恭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏耙旦。R本人自食惡果不足惜脱羡,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轻黑,春花似錦糊肤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抖拦,卻和暖如春升酣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背态罪。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工噩茄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人复颈。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓绩聘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親耗啦。 傳聞我的和親對象是個殘疾皇子凿菩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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