网页内容获取工具 HttpClient

HttpClient 简介

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。其相比于传统 JDK 自带的 URLConnection(下一篇会讲解),增加了易用性和灵活性。其功能主要是用来向服务器发送请求,并返回相关资源。在网络爬虫实战中,经常使用 HttpClient 获取网页内容,使用 jsoup 解析网页内容

HttpClient 中包含的内容较多,在本篇中主要介绍一些常用内容,相关案例程序,已上传至 Github。针对 HttpClient 中 URL 重定向处理,HttpClient 中的请求重试等内容,读者可以在以后的实战中进一步学习。

HttpClient 相关 Jar 的下载

我们首先在 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 包,如下:

1

HttpClient 的使用

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&param2=value2

为更好的讲解该方法的使用,本小节介绍一个使用 Post 方法模拟登陆人人网。从下图可以看出,需要输入用户名及密码完成登陆之后,才能查看人人网中的数据。

2

为分析登陆时,客户端向服务器提交什么参数,这里需要进行网络抓包。如下图:

3

基于抓包分析的结果,便可以模拟表单向后台发送请求,具体操作(完整代码已上传至 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 方法也常用于用户搜索内容的网络爬虫中。例如,我们需要采集京东中的搜索产品(手机),通过抓包便可以发现需要提交的参数,如下图所示:

4

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,不断的切换去访问不同的网页。

猜你喜欢

转载自blog.csdn.net/qq_37969990/article/details/81611241