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

在上一篇博客中,我们分别介绍了两种方法来进行 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 ;
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 代理,可以参看这里: 通过 httpclient 提供的 connectionsocketfactory 类来实现。

虽然使用这种方式很简单,只需要加个参数就可以了,但是其实看 httpclient 的代码注释,如下:

1
2
3
4
5
6
7
/*
* @param target    the target host for the request.
*                  implementations may accept null
*                  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 凯发k8网页登录官网还提供了几个示例,我将其作为推荐写法记录在此。

第一种写法是使用 ,如下:

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);

第二种写法是使用 ,如下:

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

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);

参考

    posted on 2016-08-02 14:11 simone 阅读(1119) 评论(0)  编辑  收藏 所属分类: java
    "));
    网站地图