Java實現Ip代理池


設置Ip代理很多時候都會有用到,尤其是在寫爬蟲相關項目的時候。雖然自己目前沒有接觸這種需求,但由於最近比較閑,就寫着當作練習吧

爬取代理IP

爬取

關於爬取代理IP,國內首先想到的網站當然是 西刺代理 。首先寫個爬蟲獲取該網站內的Ip吧。

先對 國內Http代理 標簽頁面進行爬取,解析頁面使用的Jsoup ,這里大概代碼如下

 private List<IPBean> crawl(String api, int index){
        String html = HttpUtils.getResponseContent(api + index);
        System.out.println(html);

        Document document = Jsoup.parse(html);
        Elements eles = document.selectFirst("table").select("tr");

        for (int i = 0; i < eles.size(); i++){
            if (i == 0) continue;
            Element ele = eles.get(i);
            String ip = ele.children().get(1).text();
            int port = Integer.parseInt(ele.children().get(2).text().trim());
            String typeStr = ele.children().get(5).text().trim();

            int type;
            if ("HTTP".equalsIgnoreCase(typeStr))
                type = IPBean.TYPE_HTTP;
            else
                type = IPBean.TYPE_HTTPS;

            IPBean ipBean = new IPBean(ip, port, type);
            ipList.add(ipBean);
        }
        return ipList;
    }

對某些不明白的變量,可以參考我Github
其中關鍵的就是css選擇器語法,這里需要注意的是不要亂加空格,不然會導致找不到出現空指針。
css選擇器語法具體參考這里 , 這里就不講解了。

爬取的信息包括 ip地址、端口號、和代理類型(http或https), 這三個信息我放在IPBean這個類里面。

過濾

上面爬取完成后,還要進一步過濾,篩選掉不能使用的。

篩選大概原理就是先設置上代理,然后請求某個網頁,若成功則代表此代理ip有效。
其中請求成功的標志我們可以直接獲取請求的返回碼,若為200即成功。

    /**
     * 檢測代理ip是否有效
     *
     * @param ipBean
     * @return
     */
    public static boolean isValid(IPBean ipBean) {
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipBean.getIp(), ipBean.getPort()));
        try {
            URLConnection httpCon = new URL("https://www.baidu.com/").openConnection(proxy);
            httpCon.setConnectTimeout(5000);
            httpCon.setReadTimeout(5000);
            int code = ((HttpURLConnection) httpCon).getResponseCode();
            System.out.println(code);
            return code == 200;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

注意這里要設置兩個超時,連接超時和讀取超時。連接超時還好,它默認只是有點長;然而讀取超時如果不設置,它好像就會一直阻塞着。
時間設置為5s就夠了,畢竟如果ip有效的話,會很快就請求成功的。這樣過濾后,就得到有效的代理ip了

設置代理

單次代理

單次代理表示只在這一次連接中有效,即每次都需要代理。

http方式的代理非常簡單,在URL對象的openConnection方法中加上個Proxy對象即可


 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipBean.getIp(), ipBean.getPort()));

 connection = (HttpsURLConnection) new URL(url).openConnection(proxy);

https 稍微復雜點了,中間加上了ssl協議


    /**
     * @param url
     * @param headerMap 請求頭部
     * @param ipBean
     * @return
     * @throws Exception
     */
    public static String getResponseContent(String url, Map<String, List<String>> headerMap, IPBean ipBean) throws Exception {
        HttpsURLConnection connection = null;

        // 設置代理
        if (ipBean != null) {
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipBean.getIp(), ipBean.getPort()));

            connection = (HttpsURLConnection) new URL(url).openConnection(proxy);

            if (ipBean.getType() == IPBean.TYPE_HTTPS) {
                SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
                connection.setSSLSocketFactory(sslContext.getSocketFactory());
                connection.setHostnameVerifier(new TrustAnyHostnameVerifier());
            }
        }

        if (connection == null)
            connection = (HttpsURLConnection) new URL(url).openConnection();

        // 添加請求頭部
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36");
        if (headerMap != null) {
            Iterator<Map.Entry<String, List<String>>> iterator = headerMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, List<String>> entry = iterator.next();
                List<String> values = entry.getValue();
                for (String value : values)
                    connection.setRequestProperty(entry.getKey(), value);
            }
        }

        InputStream inputStream = connection.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        StringBuilder stringBuilder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
        reader.close();
        inputStream.close();
        return stringBuilder.toString();
    }


    private static class TrustAnyTrustManager implements X509TrustManager {

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

這里https方法參考了 這篇博客

全局代理

直接上代碼,就幾行代碼

package util;

import other.IPBean;

/**
 * @author Asche
 * @github: https://github.com/asche910
 * @date 2019年1月19日
 */
public class ProxyUtils {

    /**
     * 設置全局代理
     * @param ipBean
     */
    public static void setGlobalProxy(IPBean ipBean){
        System.setProperty("proxyPort", String.valueOf(ipBean.getPort()));
        System.setProperty("proxyHost", ipBean.getIp());
        System.setProperty("proxySet", "true");
    }

}

需要注意一點就是全局只是在該java項目中生效,它不會更改系統中的代理。

檢測

設置完代理后,也可以用另外一種方法來判斷是否代理成功,即直接獲取當前ip地址。
這里我使用的是 https://www.ipip.net/ip.html 這個網站,請求獲取html后再解析得到自己的當前ip


 private static final String MY_IP_API = "https://www.ipip.net/ip.html";

    // 獲取當前ip地址,判斷是否代理成功
    public static String getMyIp() {
        try {
            String html = HttpUtils.getResponseContent(MY_IP_API);

            Document doc = Jsoup.parse(html);
            Element element = doc.selectFirst("div.tableNormal");

            Element ele = element.selectFirst("table").select("td").get(1);

            String ip = element.selectFirst("a").text();

            // System.out.println(ip);
            return ip;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

優化

emmm 優化些啥呢???

速度

爬取ip時就幾個網頁,優化估計效果不大。而真正耗時的是檢測ip是否有效,因此這里采用多線程,對每個ip的檢測請求使用一個線程,最后副線程全部結束后再統計出有多少有效ip。然而問題又來了,怎么判斷所有副線程全部結束了呢??? 腦中立刻想到的是join方法,然而仔細想想,才發現這樣並不可取。最佳方法應該是設置一個計數器,每個線程結束后計數器加一,然后在主線程循環判斷計數器的值是否與線程總數相等即可。由於涉及到並發,需要給某些方法加上鎖。這里我代碼中實現了,可以參考github

持久化

emmm 由於目前只是練練手,並沒有這樣的需求,比較懶, ( ̄▽ ̄)*
所以這個需求暫時放放吧,以后有時間再寫

最后github入口:Asche910


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM