無憂代理免費ip爬取(端口js加密)

 

原由

爲了訓練爬蟲技能(其實主要仍是js技能…),翻了可能有反爬的網站挨個摧殘,如今輪到這個網站了:http://www.data5u.com/free/index.shtmljavascript

 

解密過程

打開網站,在免費ip的列表頁查看元素選一個端口,發現表示端口的元素class屬性上有可疑的東西(代理ip類網站的反爬老是這麼沒有創意…):html

image

上面的「GEA」很像是密文存儲的東西,懷疑端口號是頁面加載完再用js計算出來填充上的,要證實的話也很簡單,只須要對照下這個元素當前的值和剛下載下來的時候值是否一致,在控制檯查看元素看到的是內存中元素的當前狀態,查看頁面源代碼的纔是頁面被下載來那一刻的狀態,右鍵-->查看網頁源代碼。搜索「49.236.220.14」,發現端口號果真不同,頁面被下載下來時是8916,如今顯示的倒是80.java

image 

解密邏輯在這個js中:http://www.data5u.com/theme/data5u/javascript/pde.js?v=1.0,原始的js進行了壓縮,使用以前寫過的展開eval的方法進行eval展開並格式化(注意須要eval展開兩次):node

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) {}
    })
})

上面這段js仍然是不可讀的,能夠看到一些關鍵詞被抽取出來放到了一個字典數組中,字典數組中的字面值還被十六進制編碼了,因此接下來須要寫點js將其轉換爲可讀形式,下面是轉換的代碼:數組

<html>
    <head></head>
    <body>
<script type="text/code-template" id="functionBody">
        $(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) {}
        })
    })
</script>
<script type="text/javascript">

    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", ''];            
    let functionBody = document.getElementById("functionBody").innerHTML;
    let readableFunctionBody = functionBody.replace(/_\$\[[0-9]+\]/g, x =>  "'" + eval(x) + "'");
    document.write(readableFunctionBody);

</script>
    </body>
</html>

轉換並格式化:網站

$(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) {}
    })
})

能夠看到解密邏輯已經很清晰了,就是把端口元素上第二個class(假定從1開始),也就是那個奇怪的字符串拿出來,而後在'ABCDEFGHIZ'中找其位置,最後把找到的位置座標按順序拼接並轉爲數字而後除以8,即獲得最終的端口號,根據解密邏輯寫出java代碼:this

private static int decodePort(String rawContent) {
    String rawNum = Stream.of(rawContent.split(""))
        .map("ABCDEFGHIZ"::indexOf)
        .map(Object::toString)
        .collect(Collectors.joining());
    return Integer.parseInt(rawNum) >> 3;
}

一個簡單的抓取demo:
編碼

package org.cc11001100.t1;

import javaslang.Tuple;
import javaslang.Tuple2;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * 這個網站的代理: http://www.data5u.com/free/index.shtml
 * 端口有加密
 *
 * @author CC11001100
 */
public class Data5UProxyGrab {

    private static int decodePort(String rawContent) {
        String rawNum = Stream.of(rawContent.split(""))
            .map("ABCDEFGHIZ"::indexOf)
            .map(Object::toString)
            .collect(Collectors.joining());
        return Integer.parseInt(rawNum) >> 3;
    }

    private static List<Tuple2<String, Integer>> parse(String url) {
        try {
            Document document = Jsoup.parse(new URL(url), 3000);
            return document.select(".wlist ul li[style=text-align:center;] ul.l2")
                .stream()
                .map(elt -> {
                    String ip = elt.select("span").first().text();
                    Elements portElt = elt.select(".port");
                    if (!portElt.isEmpty() && !portElt.html().contains("*")) {
                        String[] ss = portElt.attr("class").split("\\s+");
                        if (ss.length >= 2) {
                            return Tuple.of(ip, decodePort(ss[1]));
                        }
                    }
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(toList());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }

    /**
     * 按照國家抓取
     */
    public static List<Tuple2<String, Integer>> grabByCountry() throws IOException {
        String url = "http://www.data5u.com/free/country/%s/index.html";
        return Jsoup.parse(new URL(String.format(url, urlEncode("中國"))), 3000)
            .select("#areaDist ul.bigr span")
            .stream()
            .map(elt -> elt.attr("title"))
            .flatMap(countryName -> parse(String.format(url, urlEncode(countryName))).stream())
            .distinct()
            .collect(toList());
    }

    private static String urlEncode(String raw) {
        try {
            return URLEncoder.encode(raw, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static void main(String[] args) throws IOException {
        grabByCountry().forEach(System.out::println);
    }

}

 

更省力的方案

上面都太麻煩了,只是爲了鍛鍊一下js技能,其實觀察一下發現這個網站的功能設計得很奇怪,好比ip列表提供的篩選功能,下面被圈起來的都是能夠做爲篩選條件的:加密

image

可是恰恰沒有端口,鼠標移動到端口上點擊是沒有反應的,這是由於他要作端口加密啊,讓你知道了端口不白作了,然而木用…url

下面是分別使用幾種過濾條件時地址欄中顯示的url:

http://www.data5u.com/free/anoy/匿名/index.html
http://www.data5u.com/free/type/https/index.html
http://www.data5u.com/free/country/中國/index.html
http://www.data5u.com/free/area/雲南/index.html
http://www.data5u.com/free/isp/電信/index.html

根據以上已知基本可推出端口過濾的話多是相似於下面這種:

http://www.data5u.com/free/port/80/index.html

而後試了一下,只一次就成功了 …

image

不知道做者怎麼想的,這點不如螞蟻代理了,螞蟻代理也支持端口號篩選,不過它普通的狀況下是這樣的:

 image

端口號是用圖片顯示的,按照端口篩選是這樣的:

image

由於發請求的人已經知道端口號了,因此再圖片顯示端口號也沒用了,不如干脆將ip地址的一部分按圖片顯示,這種設計仍是比較好的,由於反爬蟲對對方已知信息增長獲取難度沒有意義,應該對其未知信息設計獲取門檻。

不過沒卵用,下一篇寫破解螞蟻代理的反爬。

相關文章
相關標籤/搜索