HttpClient4.4 登录知乎(详细过程)

引言

HttpClient是java语言下一个支持http协议的客户端编程工具包,它实现了HTTP协议的所有方法,但是不支持JS渲染。我们在做一些小玩意时,有可能需要登录某些网站获取信息,那么HttpClient就是你的好帮手,废话不多说,进入实战。

一 登录的实际意义

在HTTP横行的今天,我们每天都要登录一些网站,那么登录的意义是什么呢?首先要对cookie要有一定了解。cookie是存放在本地的一些小文件,它由服务器发送命令,浏览器在本地读写。当访问某些网站的时候,浏览器会检查是否有所浏览网站的cookie信息,如果有则在发送访问请求的时候携带上这些内容,服务器可以读取到浏览器发送请求中的cookie信息,在回应请求时可以再写cookie信息。cookie信息包括键值,内容,过期时间,所属网站。

说到这里cookie差不多讲完了,那么登录到底是怎么回事?登录就是服务器向你的浏览器写cookie,如果仅仅是在你的计算机上写cookie,那么别有用心的人伪造一个cookie也有机会登录网站,所以服务器会在内存中保留一份相同的信息,这个过程叫做session会话。如果你在网站点击退出按钮,服务器会把内存中的cookie清除掉,同时清除浏览器中有关登录的cookie。知道了这些,我们就可以上手了。

二 找到登录关键cookie

这里我们可以用wireshark来抓包分析一下。打开知乎首页,打开wireshark,开始监听端口,输入用户名和密码,点击登录,查看wireshark抓到的包。截图如下:

upload successful

upload successful

upload successful

upload successful

第一张图是浏览器post提交数据。

第二张图是提交的信息,包括_xsrf,password,remember_me,email,注意,提交的信息中包括cookie,_xsrf可以从知乎首页中获取

第三张图是服务器返回的信息,注意它的状态是200,说明是成功的

第四章图是服务器返回的数据,注意它有三条cookie设置,以及带有一个登录成功与否的信息

通过上边的步骤我们能知道什么呢?首先,发送登录请求的时候带有的cookie,以及post数据的格式,其次我们能拿到登录用cookie信息(第四张图)。

三 使用HttpClient构造登录信息

HttpClient是怎样模拟浏览器的呢?首先需要建立一个HttpClient,这个HttpClient是用来模拟一个浏览器。其次构造一个post请求,添加post数据信息以及cookie。详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import org.apache.http.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Lookup;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.impl.cookie.DefaultCookieSpecProvider;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Created by gavin on 15-7-23.
*/
public class HttpClientTest {
public static void main(String[] args)
{
//创建一个HttpClient
RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
try {
//创建一个get请求用来接收_xsrf信息
HttpGet get = new HttpGet("http://www.zhihu.com/");
//获取_xsrf
CloseableHttpResponse response = httpClient.execute(get,context);
setCookie(response);
String responseHtml = EntityUtils.toString(response.getEntity());
String xsrfValue = responseHtml.split("<input type=\"hidden\" name=\"_xsrf\" value=\"")[1].split("\"/>")[0];
System.out.println("xsrfValue:" + xsrfValue);
response.close();
//构造post数据
List<NameValuePair> valuePairs = new LinkedList<NameValuePair>();
valuePairs.add(new BasicNameValuePair("_xsrf", xsrfValue));
valuePairs.add(new BasicNameValuePair("email", "xxxx@xxx.com"));
valuePairs.add(new BasicNameValuePair("password", "xxxxx"));
valuePairs.add(new BasicNameValuePair("remember_me", "true"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(valuePairs, Consts.UTF_8);
//创建一个post请求
HttpPost post = new HttpPost("http://www.zhihu.com/login/email");
post.setHeader("Cookie", " cap_id=\"YjA5MjE0YzYyNGQ2NDY5NWJhMmFhN2YyY2EwODIwZjQ=|1437610072|e7cc307c0d2fe2ee84fd3ceb7f83d298156e37e0\"; ");
//注入post数据
post.setEntity(entity);
HttpResponse httpResponse = httpClient.execute(post);
//打印登录是否成功信息
printResponse(httpResponse);
//构造一个get请求,用来测试登录cookie是否拿到
HttpGet g = new HttpGet("http://www.zhihu.com/question/following");
//得到post请求返回的cookie信息
String c = setCookie(httpResponse);
//将cookie注入到get请求头当中
g.setHeader("Cookie",c);
CloseableHttpResponse r = httpClient.execute(g);
String content = EntityUtils.toString(r.getEntity());
System.out.println(content);
r.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void printResponse(HttpResponse httpResponse)
throws ParseException, IOException {
// 获取响应消息实体
HttpEntity entity = httpResponse.getEntity();
// 响应状态
System.out.println("status:" + httpResponse.getStatusLine());
System.out.println("headers:");
HeaderIterator iterator = httpResponse.headerIterator();
while (iterator.hasNext()) {
System.out.println("\t" + iterator.next());
}
// 判断响应实体是否为空
if (entity != null) {
String responseString = EntityUtils.toString(entity);
System.out.println("response length:" + responseString.length());
System.out.println("response content:"
+ responseString.replace("\r\n", ""));
}
}
public static Map<String,String> cookieMap = new HashMap<String, String>(64);
//从响应信息中获取cookie
public static String setCookie(HttpResponse httpResponse)
{
System.out.println("----setCookieStore");
Header headers[] = httpResponse.getHeaders("Set-Cookie");
if (headers == null || headers.length==0)
{
System.out.println("----there are no cookies");
return null;
}
String cookie = "";
for (int i = 0; i < headers.length; i++) {
cookie += headers[i].getValue();
if(i != headers.length-1)
{
cookie += ";";
}
}
String cookies[] = cookie.split(";");
for (String c : cookies)
{
c = c.trim();
if(cookieMap.containsKey(c.split("=")[0]))
{
cookieMap.remove(c.split("=")[0]);
}
cookieMap.put(c.split("=")[0], c.split("=").length == 1 ? "":(c.split("=").length ==2?c.split("=")[1]:c.split("=",2)[1]));
}
System.out.println("----setCookieStore success");
String cookiesTmp = "";
for (String key :cookieMap.keySet())
{
cookiesTmp +=key+"="+cookieMap.get(key)+";";
}
return cookiesTmp.substring(0,cookiesTmp.length()-2);
}
}

代码的流程是:

  1. 从知乎首页获取xsrf信息。
  2. post请求当中需要cookie信息,但是我们第一步中没有得到cookie,请在浏览器中自行找到cookie添加进去,上边的cookie是我找到的。
  3. 提交post请求,得到登录用cookie
  4. 随便找一个需要登录的子页面,将得到的cookie写入到请求头中,提交请求,查看是否已经登录成功

四 结果验证

upload successful

upload successful

第一张图显示得到cookie并登录成功

第二张图显示已经进入需要登录的界面

总结

  1. 当我们需要登录一个界面获取信息的时候,我们要知道登录实际上做了什么,那就是读写cookie,post数据。

  2. 获取cookie时,需要从响应头中获取,当服务器发来新的cookie信息时需要及时写入。

  3. 当我们能登录一个网站的时候,如何对其内容进行操作,这里推荐jsoup,良心库,仿jquery操作模式。