posts - 495,comments - 227,trackbacks - 0
          http://www.aneasystone.com/archives/2015/12/java-and-http-using-proxy.html

          在上一篇博客《模擬 HTTP 請求》中,我們分別介紹了兩種方法來進行 HTTP 的模擬請求:HttpURLConnectionHttpClient ,到目前為止這兩種方法都工作的很好,基本上可以實現我們需要的 GET/POST 方法的模擬。對于一個爬蟲來說,能發送 HTTP 請求,能獲取頁面數據,能解析網頁內容,這相當于已經完成 80% 的工作了。只不過對于剩下的這 20% 的工作,還得花費我們另外 80% 的時間 :-)

          在這篇博客里,我們將介紹剩下 20% 的工作中最為重要的一項:如何在 Java 中使用 HTTP 代理,代理也是爬蟲技術中的重要一項。你如果要大規模的爬別人網頁上的內容,必然會對人家的網站造成影響,如果你太拼了,就會遭人查封。要防止別人查封我 們,我們要么將自己的程序分布到大量機器上去,但是對于資金和資源有限的我們來說這是很奢侈的;要么就使用代理技術,從網上撈一批代理,免費的也好收費的 也好,或者購買一批廉價的 VPS 來搭建自己的代理服務器。關于如何搭建自己的代理服務器,后面有時間的話我再寫一篇關于這個話題的博客。現在有了一大批代理服務器之后,就可以使用我們這 篇博客所介紹的技術了。

          一、簡單的 HTTP 代理

          我們先從最簡單的開始,網上有很多免費代理,直接上百度搜索 “免費代理” 或者 “HTTP 代理” 就能找到很多(雖然網上能找到大量的免費代理,但它們的安全性已經有很多文章討論過了,也有專門的文章對此進行調研的,譬如這篇文章,我在這里就不多作說明,如果你的爬蟲爬取的信息并沒有什么特別的隱私問題,可以忽略之,如果你的爬蟲涉及一些例如模擬登錄之類的功能,考慮到安全性,我建議你還是不要使用網上公開的免費代理,而是搭建自己的代理服務器比較靠譜)。

          1.1 HttpURLConnection 使用代理

          HttpURLConnection 的 openConnection() 方法可以傳入一個 Proxy 參數,如下:

          1
          2
          3
          Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 9876));
          URL obj = new URL(url);
          HttpURLConnection con = (HttpURLConnection) obj.openConnection(proxy);

          OK 了,就這么簡單!

          不僅如此,我們注意到 Proxy 構造函數的第一個參數為枚舉類型 Proxy.Type.HTTP ,那么很顯然,如果將其修改為 Proxy.Type.SOCKS 即可以使用 SOCKS 代理。

          1.2 HttpClient 使用代理

          由于 HttpClient 非常靈活,使用 HttpClient 來連接代理有很多不同的方法。最簡單的方法莫過于下面這樣:

          1
          2
          3
          4
          HttpHost proxy = new HttpHost("127.0.0.1", 9876, "HTTP");
          CloseableHttpClient httpclient = HttpClients.createDefault();
          HttpGet request = new HttpGet(url);
          CloseableHttpResponse response = httpclient.execute(proxy, request);

          和上一篇中使用 HttpClient 發送請求的代碼幾乎一樣,只是 httpclient.execute() 方法多加了一個參數,第一參數為 HttpHost 類型,我們這里設置成我們的代理即可。

          這里要注意一點的是,雖然這里的 new HttpHost() 和上面的 new Proxy() 一樣,也是可以指定協議類型的,但是遺憾的是 HttpClient 默認是不支持 SOCKS 協議的,如果我們使用下面的代碼:

          1
          HttpHost proxy = new HttpHost("127.0.0.1", 1080, "SOCKS");

          將會直接報協議不支持異常:

          org.apache.http.conn.UnsupportedSchemeException: socks protocol is not supported

          如果希望 HttpClient 支持 SOCKS 代理,可以參看這里:How to use Socks 5 proxy with Apache HTTP Client 4? 通過 HttpClient 提供的 ConnectionSocketFactory 類來實現。

          雖然使用這種方式很簡單,只需要加個參數就可以了,但是其實看 HttpClient 的代碼注釋,如下:

          1
          2
          3
          4
          5
          6
          7
          /*
          * @param target    the target host for the request.
          *                  Implementations may accept <code>null</code>
          *                  if they can still determine a route, for example
          *                  to a default target or by inspecting the request.
          * @param request   the request to execute
          */

          可以看到第一個參數 target 并不是代理,它的真實作用是 執行請求的目標主機,這個解釋有點模糊,什么叫做 執行請求的目標主機?代理算不算執行請求的目標主機呢?因為按常理來講,執行請求的目標主機 應該是要請求 URL 對應的站點才對。如果不算的話,為什么這里將 target 設置成代理也能正常工作?這個我也不清楚,還需要進一步研究下 HttpClient 的源碼來深入了解下。

          除了上面介紹的這種方式(自己寫的,不推薦使用)來使用代理之外,HttpClient 官網還提供了幾個示例,我將其作為推薦寫法記錄在此。

          第一種寫法是使用 RequestConfig 類,如下:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          CloseableHttpClient httpclient = HttpClients.createDefault();      
          HttpGet request = new HttpGet(url);
           
          request.setConfig(
              RequestConfig.custom()
                  .setProxy(new HttpHost("45.32.21.237", 8888, "HTTP"))
                  .build()
          );
                   
          CloseableHttpResponse response = httpclient.execute(request);

          第二種寫法是使用 RoutePlanner 類,如下:

          1
          2
          3
          4
          5
          6
          7
          HttpHost proxy = new HttpHost("127.0.0.1", 9876, "HTTP");
          DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
          CloseableHttpClient httpclient = HttpClients.custom()
                  .setRoutePlanner(routePlanner)
                  .build();
          HttpGet request = new HttpGet(url);
          CloseableHttpResponse response = httpclient.execute(request);

          二、使用系統代理配置

          我們在調試 HTTP 爬蟲程序時,常常需要切換代理來測試,有時候直接使用系統自帶的代理配置將是一種簡單的方法。以前在做 .Net 項目時,程序默認使用 Internet 網絡設置中配的代理,遺憾的是,我這里說的系統代理配置指的 JVM 系統,而不是操作系統,我還沒找到簡單的方法來讓 Java 程序直接使用 Windows 系統下的代理配置。

          盡管如此,系統代理使用起來還是很簡單的。一般有下面兩種方式可以設置 JVM 的代理配置:

          2.1 System.setProperty

          Java 中的 System 類不僅僅是用來給我們 System.out.println() 打印信息的,它其實還有很多靜態方法和屬性可以用。其中 System.setProperty() 就是比較常用的一個。

          可以通過下面的方式來分別設置 HTTP 代理,HTTPS 代理和 SOCKS 代理:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          // HTTP 代理,只能代理 HTTP 請求
          System.setProperty("http.proxyHost", "127.0.0.1");
          System.setProperty("http.proxyPort", "9876");
           
          // HTTPS 代理,只能代理 HTTPS 請求
          System.setProperty("https.proxyHost", "127.0.0.1");
          System.setProperty("https.proxyPort", "9876");
           
          // SOCKS 代理,支持 HTTP 和 HTTPS 請求
          // 注意:如果設置了 SOCKS 代理就不要設 HTTP/HTTPS 代理
          System.setProperty("socksProxyHost", "127.0.0.1");
          System.setProperty("socksProxyPort", "1080");

          這里有三點要說明:

          1. 系統默認先使用 HTTP/HTTPS 代理,如果既設置了 HTTP/HTTPS 代理,又設置了 SOCKS 代理,SOCKS 代理會起不到作用
          2. 由于歷史原因,注意 socksProxyHostsocksProxyPort 中間沒有小數點
          3. HTTP 和 HTTPS 代理可以合起來縮寫,如下:
          1
          2
          3
          // 同時支持代理 HTTP/HTTPS 請求
          System.setProperty("proxyHost", "127.0.0.1");
          System.setProperty("proxyPort", "9876");

          2.2 JVM 命令行參數

          可以使用 System.setProperty() 方法來設置系統代理,也可以直接將這些參數通過 JVM 的命令行參數來指定。如果你使用的是 Eclipse ,可以按下面的步驟來設置:

          1. 按順序打開:Window -> Preferences -> Java -> Installed JREs -> Edit
          2. 在 Default VM arguments 中填寫參數: -DproxyHost=127.0.0.1 -DproxyPort=9876

          jvm-arguments.jpg

          2.3 使用系統代理

          上面兩種方法都可以設置系統,下面要怎么在程序中自動使用系統代理呢?

          對于 HttpURLConnection 類來說,程序不用做任何變動,它會默認使用系統代理。但是 HttpClient 默認是不使用系統代理的,如果想讓它默認使用系統代理,可以通過 SystemDefaultRoutePlannerProxySelector 來設置。示例代碼如下:

          1
          2
          3
          4
          5
          6
          SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
          CloseableHttpClient httpclient = HttpClients.custom()
                  .setRoutePlanner(routePlanner)
                  .build();
          HttpGet request = new HttpGet(url);    
          CloseableHttpResponse response = httpclient.execute(request);

          參考

          1. HttpClient Tutorial
          2. 評測告訴你:那些免費代理悄悄做的齷蹉事兒
          3. How to use Socks 5 proxy with Apache HTTP Client 4?
          4. 使用ProxySelector選擇代理服務器
          5. Java Networking and Proxies
          posted on 2016-08-02 14:11 SIMONE 閱讀(1250) 評論(0)  編輯  收藏 所屬分類: JAVA
          主站蜘蛛池模板: 泾阳县| 崇州市| 萨迦县| 界首市| 姜堰市| 兰州市| 金乡县| 邻水| 香河县| 资阳市| 安新县| 临邑县| 金堂县| 宜良县| 房产| 土默特左旗| 东乌珠穆沁旗| 濮阳县| 丹江口市| 邹平县| 青田县| 阿巴嘎旗| 依安县| 竹北市| 汉源县| 柘荣县| 宜阳县| 民和| 白水县| 偃师市| 府谷县| 阿尔山市| 疏勒县| 社旗县| 龙井市| 宁海县| 台江县| 迁安市| 青田县| 商城县| 十堰市|