在上一篇博客《模擬 HTTP 請求》中,我們分別介紹了兩種方法來進行 HTTP 的模擬請求:HttpURLConnection
和 HttpClient
,到目前為止這兩種方法都工作的很好,基本上可以實現(xiàn)我們需要的 GET/POST 方法的模擬。對于一個爬蟲來說,能發(fā)送 HTTP 請求,能獲取頁面數(shù)據(jù),能解析網(wǎng)頁內(nèi)容,這相當于已經(jīng)完成 80% 的工作了。只不過對于剩下的這 20% 的工作,還得花費我們另外 80% 的時間 :-)
在這篇博客里,我們將介紹剩下 20% 的工作中最為重要的一項:如何在 Java 中使用 HTTP 代理,代理也是爬蟲技術(shù)中的重要一項。你如果要大規(guī)模的爬別人網(wǎng)頁上的內(nèi)容,必然會對人家的網(wǎng)站造成影響,如果你太拼了,就會遭人查封。要防止別人查封我 們,我們要么將自己的程序分布到大量機器上去,但是對于資金和資源有限的我們來說這是很奢侈的;要么就使用代理技術(shù),從網(wǎng)上撈一批代理,免費的也好收費的 也好,或者購買一批廉價的 VPS 來搭建自己的代理服務(wù)器。關(guān)于如何搭建自己的代理服務(wù)器,后面有時間的話我再寫一篇關(guān)于這個話題的博客。現(xiàn)在有了一大批代理服務(wù)器之后,就可以使用我們這 篇博客所介紹的技術(shù)了。
一、簡單的 HTTP 代理
我們先從最簡單的開始,網(wǎng)上有很多免費代理,直接上百度搜索 “免費代理” 或者 “HTTP 代理” 就能找到很多(雖然網(wǎng)上能找到大量的免費代理,但它們的安全性已經(jīng)有很多文章討論過了,也有專門的文章對此進行調(diào)研的,譬如這篇文章,我在這里就不多作說明,如果你的爬蟲爬取的信息并沒有什么特別的隱私問題,可以忽略之,如果你的爬蟲涉及一些例如模擬登錄之類的功能,考慮到安全性,我建議你還是不要使用網(wǎng)上公開的免費代理,而是搭建自己的代理服務(wù)器比較靠譜)。
1.1 HttpURLConnection 使用代理
HttpURLConnection 的 openConnection()
方法可以傳入一個 Proxy 參數(shù),如下:
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 構(gòu)造函數(shù)的第一個參數(shù)為枚舉類型 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 發(fā)送請求的代碼幾乎一樣,只是 httpclient.execute()
方法多加了一個參數(shù),第一參數(shù)為 HttpHost
類型,我們這里設(shè)置成我們的代理即可。
這里要注意一點的是,雖然這里的 new HttpHost()
和上面的 new Proxy()
一樣,也是可以指定協(xié)議類型的,但是遺憾的是 HttpClient 默認是不支持 SOCKS 協(xié)議的,如果我們使用下面的代碼:
1 | HttpHost proxy = new HttpHost( "127.0.0.1" , 1080 , "SOCKS" );
|
將會直接報協(xié)議不支持異常:
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 類來實現(xiàn)。
雖然使用這種方式很簡單,只需要加個參數(shù)就可以了,但是其實看 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
*/
|
可以看到第一個參數(shù) target 并不是代理,它的真實作用是 執(zhí)行請求的目標主機,這個解釋有點模糊,什么叫做 執(zhí)行請求的目標主機?代理算不算執(zhí)行請求的目標主機呢?因為按常理來講,執(zhí)行請求的目標主機 應(yīng)該是要請求 URL 對應(yīng)的站點才對。如果不算的話,為什么這里將 target 設(shè)置成代理也能正常工作?這個我也不清楚,還需要進一步研究下 HttpClient 的源碼來深入了解下。
除了上面介紹的這種方式(自己寫的,不推薦使用)來使用代理之外,HttpClient 官網(wǎng)還提供了幾個示例,我將其作為推薦寫法記錄在此。
第一種寫法是使用 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);
|
二、使用系統(tǒng)代理配置
我們在調(diào)試 HTTP 爬蟲程序時,常常需要切換代理來測試,有時候直接使用系統(tǒng)自帶的代理配置將是一種簡單的方法。以前在做 .Net 項目時,程序默認使用 Internet 網(wǎng)絡(luò)設(shè)置中配的代理,遺憾的是,我這里說的系統(tǒng)代理配置指的 JVM 系統(tǒng),而不是操作系統(tǒng),我還沒找到簡單的方法來讓 Java 程序直接使用 Windows 系統(tǒng)下的代理配置。
盡管如此,系統(tǒng)代理使用起來還是很簡單的。一般有下面兩種方式可以設(shè)置 JVM 的代理配置:
2.1 System.setProperty
Java 中的 System
類不僅僅是用來給我們 System.out.println()
打印信息的,它其實還有很多靜態(tài)方法和屬性可以用。其中 System.setProperty()
就是比較常用的一個。
可以通過下面的方式來分別設(shè)置 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 請求
// 注意:如果設(shè)置了 SOCKS 代理就不要設(shè) HTTP/HTTPS 代理
System.setProperty( "socksProxyHost" , "127.0.0.1" );
System.setProperty( "socksProxyPort" , "1080" );
|
這里有三點要說明:
- 系統(tǒng)默認先使用 HTTP/HTTPS 代理,如果既設(shè)置了 HTTP/HTTPS 代理,又設(shè)置了 SOCKS 代理,SOCKS 代理會起不到作用
- 由于歷史原因,注意
socksProxyHost
和 socksProxyPort
中間沒有小數(shù)點 - HTTP 和 HTTPS 代理可以合起來縮寫,如下:
1 2 3 | // 同時支持代理 HTTP/HTTPS 請求
System.setProperty( "proxyHost" , "127.0.0.1" );
System.setProperty( "proxyPort" , "9876" );
|
2.2 JVM 命令行參數(shù)
可以使用 System.setProperty()
方法來設(shè)置系統(tǒng)代理,也可以直接將這些參數(shù)通過 JVM 的命令行參數(shù)來指定。如果你使用的是 Eclipse ,可以按下面的步驟來設(shè)置:
- 按順序打開:Window -> Preferences -> Java -> Installed JREs -> Edit
- 在 Default VM arguments 中填寫參數(shù):
-DproxyHost=127.0.0.1 -DproxyPort=9876

2.3 使用系統(tǒng)代理
上面兩種方法都可以設(shè)置系統(tǒng),下面要怎么在程序中自動使用系統(tǒng)代理呢?
對于 HttpURLConnection
類來說,程序不用做任何變動,它會默認使用系統(tǒng)代理。但是 HttpClient
默認是不使用系統(tǒng)代理的,如果想讓它默認使用系統(tǒng)代理,可以通過 SystemDefaultRoutePlanner
和 ProxySelector
來設(shè)置。示例代碼如下:
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);
|
參考
- HttpClient Tutorial
- 評測告訴你:那些免費代理悄悄做的齷蹉事兒
- How to use Socks 5 proxy with Apache HTTP Client 4?
- 使用ProxySelector選擇代理服務(wù)器
- Java Networking and Proxies
posted on 2016-08-02 14:11
SIMONE 閱讀(1234)
評論(0) 編輯 收藏 所屬分類:
JAVA