我只是想查个电费(3)

玄学人生波澜壮阔:Cookie的设置和使用

Posted by Donggu Ho on 2016-11-22

突然玄学

上接终于选好了校区、楼栋和楼层。然后,只需要在请求中加上房间信息、查询类型以及操作类型(“查询”或“重置”)就可以跳转到结果页了。

根据 Postman 调试,需要添加的键值包括这些(以及上一步得到的__VIEWSTATE):

1
2
3
4
drfangjian:     957         //房间的value
radio: usedR //选择【用电记录】选项
ImageButton1.x: 39 //其实是鼠标点击“查询”时相对于按钮左上角的坐标。
ImageButton1.y: 13 //只要传了就行,值是多少无所谓

但是当发送了请求后却并没有获取到想要的结果页……而且还被丢回了登录页怎么回事?再次比对 Chrome 的网络信息可以发现,在点下登录后的 post,其response的状态码为302重定向,header中有Location指明跳转位置,而且还有一条Set-Cookie属性分派了 sessionId。浏览器在收到重定向回答后向跳转目标地址发送GET请求,并将获得的 cookie 作为头信息加入 GET 请求中,才能得到结果页。

这里必须跪一下 Postman, 在 Postman 上的调试的确是直接发 post 就能获得结果,而且能查看到的 response 和 cookie 中都没有记录……



那么现在问题很好办,把 post 请求中的set-cookie属性值保存下来,然后再发一条 get 请求就好了。先在 OkHttp 中查看 response 的 Headers 信息:

1
2
Response response = client.newCall(request).execute();
System.out.println(response.headers());

然后打印输出如下:

1
2
3
4
5
6
7
Cache-Control: private
Content-Type: text/html; charset=gb2312
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 21 Nov 2016 15:22:36 GMT
Content-Length: 2892

……既没有Location也没有Set-Cookie也没有Cookie啊Σ( ° △ °|||)︴
只能打开调试查看了。在执行请求这行设置断点,查看response变量信息。

1
Response response = client.newCall(request).execute();      //在这行设置断点

调试台看到response的值如下

response中有一个成员变量priorResponse,其状态码正是302!看来这就是我们需要的那个Response?查看其header:

然而并不是(;′⌒`)从Location可知,这是一个要求浏览器重定向到登录页的回答,而且 headers 中也没有cookie相关的信息。但展开查看这个 response,我们可以发现它还有一个priorResponse:状态码302,header 中有set-cookie属性,而且跳转 location 为usedRecord.aspx

这不正是我们在找的那个response吗!
那么其实已经梳理清楚:客户端发送 post 后,服务器返回了重定向要求和 cookie;于是客户端自动再次向目标页发送请求,但由于没有在头部添加 cookie 信息,服务器又再次发送重定向回答让其返回到登录页。
……这种很蠢的感觉是怎么回事。
……总之,只要让它在得到回应时把 cookie 存下来就好了。

OkHttp3 其实有 Cookie 持久化的管理模块,开源,可以实现Cookie的自动化持久化管理。我这个项目挺小的就懒得弄这些了,其实是没看懂,直接在 response 中获取 cookie 然后设置就好。方法是停止客户端的自动重定向行为,这样才能获取到第一个 response。修改OkHttpClient的新建语句:

1
2
3
4
5
6
7
//原语句
//private OkHttpClient client = new OkHttpClient();

//使用Builder新建
private client = new OkHttpClient.Builder()
.followRedirects(false) //关闭跟随重定向
.build();

使用OkHttpClient.Builder函数进行新建,将重定向设置为false。现在再发送请求,客户端不会自动跳转,获取的就是含有Set-Cookie的回答了。

1
2
3
4
5
6
7
8
String mycookie = response.header("set-cookie");
Request request = new Request.Builder()
.url("http://202.120.163.129:88/usedRecord.aspx")
.get()
.addHeader("cookie", mycookie)
.build();

Response response = client.newCall(request).execute();

这样,就可以得到结果页了,后续只要将余额信息提取即可。

Jsoup 实在是太方便了根本没有什么好港的啊!不过,Jsoup 通常使用的get()或者post()会直接返回解析 response body 后生成的树状Document,那么 header 信息就无法获取了。因此如果需要头部信息的话,需要使用execute()方法来返回 Response 对象:

1
2
3
4
5
Connection.Response response = Jsoup.connect(url)
.requestBody(postBody+"&__VIEWSTATE="+viewstate)
.followRedirects(false) //关闭自动跳转
.method(Connection.Method.POST) //使用method指定请求方法
.execute();

这样获取到的就是完整的 Response 对象,老实说我觉得 Jsoup 和 OkHttp 的语法还挺像的……但 Jsoup 似乎已经内置了 Cookie 的保存,因为查看 response header 并没有Set-Cookie,但输出response.cookies()会发现Cookie已经存好了,只需要再下次请求时加上即可:

1
2
3
4
Document doc = Jsoup.connect(url+response.header("Location"))
.cookies(response.cookies())
.get();
String balance = doc.select(".orange").first().text();

即可获得余额。总之在爬数据方面Jsoup真是比OkHttp方便好多啊(语法上)。。。于是后面开发安卓APP的时候我就用Jsoup了。