赞
踩
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。其相比于传统 JDK 自带的 URLConnection(下一篇会讲解),增加了易用性和灵活性。其功能主要是用来向服务器发送请求,并返回相关资源。在网络爬虫实战中,经常使用 HttpClient 获取网页内容,使用 jsoup 解析网页内容。
HttpClient 中包含的内容较多,在本篇中主要介绍一些常用内容,相关案例程序,已上传至 Github。针对 HttpClient 中 URL 重定向处理,HttpClient 中的请求重试等内容,读者可以在以后的实战中进一步学习。
我们首先在 MVNRepository 中搜索 HttpClient。接着,使用 Eclipse 或其他工具构建 Maven 工程,使用 Maven 工程中的 pom.xml 下载 HttpClient 相关依赖 Jar 包。本篇以最新版 HttpClient 配置为例:
- <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>4.5.5</version>
- </dependency>
配置完成,保存 pom.xml 文件后,会发现工程自动下载了 HttpClient 依赖的相关 Jar 包,如下:
get() 方法的使用
在 HttpClient 中,提供了 get() 请求 URL 的方法。作为使用者,只需提供一个执行请求对象,HttpClient 便会向目标服务器发送 GET 请求,并返回相应的响应结果,或者请求失败时会抛出异常。在本小节主要介绍几种常用的操作。
在使用 HttpClient 之前,首先要对其进行初始化操作。接着,给定一个 URL 确定需要使用的请求方法,例如 get 方法(可以通过抓包,观察请求方法),然后向服务器发送请求,服务器接收到并解析了客户端的请求信息后,返回 HTTP 响应给客户端。HTTP 响应信息包括协议版本号,状态码等。以下为一个案例程序,请求的是 W3school 上的一个网址。
- HttpClient client = new DefaultHttpClient(); //初始化httpclient
- String personalUrl = "http://www.w3school.com.cn/b.asp"; //请求的地址URL
- HttpGet getMethod = new HttpGet(personalUrl); // get方法请求
- HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
- HttpStatus.SC_OK, "OK"); //初始化HTTP响应
- response = client.execute(getMethod); //执行响应
- String status = response.getStatusLine().toString(); //响应状态
- int StatusCode = response.getStatusLine().getStatusCode(); //获取响应状态码
- ProtocolVersion protocolVersion = response.getProtocolVersion(); //协议的版本号
- String phrase = response.getStatusLine().getReasonPhrase(); //是否ok
- if(StatusCode == 200){ //状态码200表示响应成功
- //获取实体内容,这里为HTML内容
- String entity = EntityUtils.toString (response.getEntity(),"gbk");
- //输出实体内容
- System.out.println(entity);
- EntityUtils.consume(response.getEntity()); //消耗实体
- }else {
- //关闭HttpEntity的流实体
- EntityUtils.consume(response.getEntity()); //消耗实体
- }
这里使用 HttpClient client = new DefaultHttpClient()
进行初始化,目前该方法已有些过时,但仍然是可以用的。另外,我们也可以使用其他的初始化方式,以下提供两种:
- HttpClient httpClient = HttpClients.custom().build(); //初始化httpclient
- HttpGet httpGet = new HttpGet("http://www.w3school.com.cn/b.asp");
- HttpResponse httpResponse = null;//请求响应
- try {
- httpResponse = httpClient.execute(httpGet);
- } catch (IOException e) {
- e.printStackTrace();
- }
- HttpEntity httpEntity = httpResponse.getEntity();
- System.out.println("get code"+httpResponse.getStatusLine().getStatusCode());
- String entity = EntityUtils.toString(httpEntity,"gbk"); //注意这里设置编码
- System.out.println(entity);
- CloseableHttpClient httpClient = HttpClients.createDefault(); //初始化Httpclient
- HttpGet httpGet=new HttpGet("http://www.w3school.com.cn/b.asp");
- CloseableHttpResponse response = null;//请求响应
- try {
- response = httpClient.execute(httpGet);
- } catch (IOException e) {
- e.printStackTrace();
- }
- String entity = EntityUtils.toString (response.getEntity(),"gbk");
- System.out.println(entity);
- response.close();
post() 方法的使用
直接使用 post 方法类似于上小节介绍的 get 方法,只是将 HttpGet httpGet=new HttpGet(url) 改成 HttpPost httpPost=new HttpPost(url)
。但在本节我将重点介绍 HTML 表单请求方面的内容。模拟表单请求在爬虫中常用来模拟登陆 Web 应用。在 HttpClient 中,提供了实体类 UrlEncodedFormEntity 以方便处理该过程,其使用方法如下:
- List<NameValuePair> nvps= new ArrayList<NameValuePair>();
- nvps.add(new BasicNameValuePair("param1", "value1"));
- nvps.add(new BasicNameValuePair("param2", "value2"));
- UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nvps, Consts.UTF_8);
- HttpPost httppost = new HttpPost("http://localhost/handler.do");
- httppost.setEntity(entity);
UrlEncodedFormEntity 实例将会使用 URL encoding 来编码参数,产生以下内容:
param1=value1¶m2=value2
为更好的讲解该方法的使用,本小节介绍一个使用 Post 方法模拟登陆人人网。从下图可以看出,需要输入用户名及密码完成登陆之后,才能查看人人网中的数据。
为分析登陆时,客户端向服务器提交什么参数,这里需要进行网络抓包。如下图:
基于抓包分析的结果,便可以模拟表单向后台发送请求,具体操作(完整代码已上传至 Github)如下:
- HttpClient httpclient = HttpClients.custom().build(); //初始化httpclient
- String renRenLoginURL = "http://www.renren.com/PLogin.do"; //登陆的地址
- HttpPost httpost = new HttpPost(renRenLoginURL); //采用post方法
- //建立一个NameValuePair数组,用于存储欲传送的参数
- List<NameValuePair> nvps = new ArrayList<NameValuePair>();
- nvps.add(new BasicNameValuePair("origURL","http://www.renren.com/home"));
- nvps.add(new BasicNameValuePair("email", "你的邮箱地址"));
- nvps.add(new BasicNameValuePair("password", "你的登陆密码"));
- HttpResponse response = null;
- try {
- //表单参数提交
- httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
- response = httpclient.execute(httpost);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- } finally {
- httpost.abort();
- }
通过抓包,可以看到登陆人人网需要提交一些参数,但核心的参数是用户名及密码,从上面的程序中便可发现。另外,Post 方法也常用于用户搜索内容的网络爬虫中。例如,我们需要采集京东中的搜索产品(手机),通过抓包便可以发现需要提交的参数,如下图所示:
Header 信息的使用
HTTP 的请求头是客户端每次向服务器发送请求时,传递的一组属性和配置信息。在前面的章节中,已经介绍了请求头的类型。在网络爬虫中,设置请求头信息的好处,便是可以模拟浏览器行为,从而起到一定的防爬功能。当然,对于一些防爬措施很差的网址也可不做设置。在本小节中,主要介绍 HttpClient 在使用的过程中,如何设置请求头。以下仍以 W3school 上的一个网址(http://www.w3school.com.cn/b.asp)为例。同样,我们首先通过抓包的方式查看请求该网站需要的请求头信息,抓包结果如下图所示:
以下为设置设置请求头的一种方式:
- HttpClient httpClient = HttpClients.custom().build(); //初始化httpclient
- HttpGet httpget = new HttpGet("http://www.w3school.com.cn/b.asp"); //使用的请求方法
- //请求头配置
- httpget.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
- httpget.setHeader("Accept-Encoding", "gzip, deflate");
- httpget.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
- httpget.setHeader("Cache-Control", "max-age=0");
- httpget.setHeader("Host", "www.w3school.com.cn");
- httpget.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36"); //这项内容很重要
- HttpResponse response = httpClient.execute(httpget); //发出get请求
- //获取响应状态码
- int code = response.getStatusLine().getStatusCode();
- HttpEntity httpEntity = response.getEntity(); //获取网页内容流
- String entity = EntityUtils.toString(httpEntity, "gbk"); //以字符串的形式(需设置编码)
- System.out.println(code + "\n" + entity); //输出所获得的的内容
- EntityUtils.consume(httpEntity); //关闭内容流
另外,也可使用另外一种方式添加头信息:
- List<Header> headerList = new ArrayList<Header>(); //通过集合封装头信息
- headerList.add(new BasicHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"));
- headerList.add(new BasicHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36"));
- headerList.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate"));
- headerList.add(new BasicHeader(HttpHeaders.CACHE_CONTROL, "max-age=0"));
- headerList.add(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive"));
- headerList.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9"));
- headerList.add(new BasicHeader(HttpHeaders.HOST, "www.w3school.com.cn"));
- //构造自定义的HttpClient对象
- HttpClient httpClient = HttpClients.custom().setDefaultHeaders(headerList).build();
- HttpGet httpget = new HttpGet("http://www.w3school.com.cn/b.asp"); //使用的请求方法
- //获取结果
- HttpResponse response = httpClient.execute(httpget); //发出get请求
在实战中,为了防止网络爬虫被封,我们往往需要设置多个 User-Agent
,在请求网站的多个页面时,可以自由的从这些 User-Agent
随机切换,进而达到模拟人行为的请求。
HttpClient 中的超时问题
在网络爬虫中,有时也需要设置连接超时和获取数据超时,原因是超时会影响程序的继续运行。HttpClient 中可设置三个超时:RequestTimeout(从连接池中获取连接的超时时间)、ConnectTimeout(建立连接的超时)、SocketTimeout(请求获取数据的超时时间)。其使用方式如下:
- //全部设置为10秒
- RequestConfig requestConfig = RequestConfig.custom()
- .setSocketTimeout(10000).setConnectTimeout(10000).setConnectionRequestTimeout(10000).build();
- //配置HttpClient
- CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
- CloseableHttpResponse response = null;
- try {
- response = httpClient.execute(new HttpGet("http://www.w3school.com.cn/b.asp")); //使用get方法发送请求
- }catch (Exception e){
- e.printStackTrace();
- }
- String result = EntityUtils.toString(response.getEntity(),"gbk"); //获取结果,html
- System.out.println(result); //输出结果
另外,也可针对 HttpPost 或 HttpGet 设置超时,使用方式如下:
- CloseableHttpClient httpClient = HttpClients.createDefault(); //初始化httpClient
- HttpGet httpGet=new HttpGet("http://www.w3school.com.cn/b.asp");//Get请求(Post方法相似)
- RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();//设置请求和传输超时时间
- httpGet.setConfig(requestConfig); //httpget信息配置
- CloseableHttpResponse response = null;
- try {
- response = httpClient.execute(httpGet); //使用get方法发送请求
- }catch (Exception e){
- e.printStackTrace();
- }
- String result = EntityUtils.toString(response.getEntity(),"gbk"); //获取结果,html
- System.out.println(result); //输出结果
代理服务器的使用
代理服务器(Proxy Server)是网上提供转接功能的服务器,一般情况下,我们使用网络浏览器直接连接其他Internet站点取得网络信息。代理服务器是介于客户端和 Web 服务器之间的另一台服务器,基于代理,浏览器不再直接向 Web 服务器取数据,而是向代理服务器发出请求,信号会先送到代理服务器,由代理服务器取回浏览器所需要的信息。简单的可以理解为中介。
在网络爬虫中,使用代理服务器访问站点内容,能够隐藏爬虫的真实 IP 地址,从而防止网络爬虫被封。另外,普通网络爬虫使用固定 IP 请求时,往往需要设置随机休息时间,而通过代理却不需要,从而提高了爬虫的效率。目前,代理可以来源于提供免费代理地址以及接口的网站(例如https://free-proxy-list.net/),但这些免费的代理 IP 都是不稳定的。另外,也可通过购买的方式使用代理,其提供的代理 IP 可用率较高,稳定性较强。在本小节中,我介绍在 HttpClient 中如何基于代理 IP 来请求网页,我仍以请求 W3school 的网址为例:
- RequestConfig defaultRequestConfig = RequestConfig.custom()
- .setProxy(new HttpHost("171.97.67.160", 3128, null))
- .build(); //添加代理
- HttpGet httpGet = new HttpGet("http://www.w3school.com.cn/b.asp"); //设置请求的方式及网页
- HttpClient httpClient = HttpClients.custom().
- setDefaultRequestConfig(defaultRequestConfig).build(); //配置httpClient
- HttpResponse httpResponse = httpClient.execute(httpGet); //执行请求
- System.out.println("状态码为:"+httpResponse.getStatusLine().getStatusCode());
- String result = EntityUtils.toString(httpResponse.getEntity(),"gbk"); //获取结果,html
- System.out.println(result); //输出结果
在程序中,代理添加包括其 IP 地址以及端口。该案例,只是使用了一个代理 IP。在实际应用中,我们往往需要使用很多的代理 IP,不断的切换去访问不同的网页。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。