文档章节

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

jiangmitiao
 jiangmitiao
发布于 2015/07/23 20:53
字数 1456
阅读 7457
收藏 218

引言

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

 

一 登录的实际意义

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

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

 

二 找到登录关键cookie

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

第一张图是本地post提交数据。

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

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

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

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

 

三 使用HttpClient构造登录信息

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

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写入到请求头中,提交请求,查看是否已经登录成功

 

四 结果验证

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

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

 

总结

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

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

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

更多文章:https://blog.gavinzh.com

© 著作权归作者所有

共有 人打赏支持
jiangmitiao

jiangmitiao

粉丝 16
博文 48
码字总数 44413
作品 1
朝阳
程序员
加载中

评论(13)

寻梦丶
寻梦丶
楼主这个项目还在么,可以发给我一份吗wancihua@qq.com,谢谢啦54
byronhs
byronhs
不错,收藏了,下次登陆校园的网的时候来借鉴下28
jiangmitiao
jiangmitiao

引用来自“立足点感CK”的评论

我是初学者一直在研究怎么模拟登录方正系统获取课程表,没啥进展,求指导
应该是正方系统吧?打开登录首页,看一下form表单,是不是有一个类似_xsrf的隐藏域。登录试一试,看一下network中有没有post提交的数据,对比一下,了解都提交什么信息。打开resource,看一下cookie有什么变化。流程就是这样子的,具体情况你再试试,记得用wireshark抓包对比浏览器登录和你的程序登录有什么差别,因为你的程序没法显示登陆流程。
立足点感CK
立足点感CK
我是初学者一直在研究怎么模拟登录方正系统获取课程表,没啥进展,求指导
jiangmitiao
jiangmitiao

引用来自“那位先生”的评论

请问一下第57行的 post.setHeader("Cookie"....为什么我这里也可以用?这个cookie的值是可以随意么?
他是对一个字符串的加密,这个值可以在你的浏览器cookie中取得,但是不能自己捏造
那位先生
那位先生
请问一下第57行的 post.setHeader("Cookie"....为什么我这里也可以用?这个cookie的值是可以随意么?
泥沙砖瓦浆木匠
泥沙砖瓦浆木匠
20 Java的来看看
jiangmitiao
jiangmitiao

引用来自“iman123”的评论

现在似乎变化了,没有登陆过访问www.zhihu.com,设置的cookit如"_za=e9994b0e-322a-4a87-83ff-7f721c7ee1a5; __utmt=1; __utma=51854390.32114769.1437791837.1437791837.1437791837.1; __utmb=51854390.2.10.1437791837; __utmc=51854390; __utmz=51854390.1437791837.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmv=51854390.000--|3=entry_date=20150725=1",
恩,如果没有登录过,cookie不一样
jiangmitiao
jiangmitiao

引用来自“游客”的评论

意义何在啊
看你想做什么了
游客
游客
意义何在啊
OSChina 开源周刊 44 期

每周技术抢先看,总有你想要的! 移动开发 【开发】使用 Countly 来分析 Apple Watch 统计数据 【翻译】Xcode 7 中的 UI 测试功能 前端开发 【开源访谈】大漠穷秋:AngularJS 发展与开源精神...

OSC编辑部
2015/07/25
1K
2
Scrapy爬虫教程三 详细的Python Scrapy模拟登录知乎

Scrapy爬虫教程一 Windows下安装Scrapy的方式和问题总结 Scrapy爬虫教程二 浅析最烦人的反爬虫手段 Scrapy爬虫教程三 详细的Python Scrapy模拟登录知乎 Scrapy爬虫教程四 Scrapy+Selenium有浏...

熊熊熊孩子
2017/11/16
0
0
用Zhihu-OAuth库轻松爬取知乎各类数据

去年(手动滑稽)某日想爬取知乎特定话题下的全部回答数据,于是翻出以前写的知乎话题爬虫跑运行了下,没想到遇到一个一直没能解决的bug,貌似是抓包到的API返回的JSON数据格式有问题,于是遭...

Deserts_X
01/02
0
0
爬虫课程(十一)|知乎:使用Scrapy模拟登录知乎

前面爬虫课程七、八、九、十,我把爬虫豆瓣读书的爬虫讲解完毕啦,我们很顺利地爬取了豆瓣读书书籍内容,爬取过程中也不需要用户登陆。 然而,有些时候,我们要爬取某些信息时是需要我们在登...

小怪聊职场
2017/11/10
0
0
Python 开发网络爬虫(四): 登录

http://blog.jobbole.com/77878/ 博客 – 伯乐在线 首页最新文章在线课程业界开发 IT技术 设计 创业IT职场投稿更多 » 伯乐在线 > 首页 > 所有文章 > Python > 零基础自学用Python 3开发网络...

王一字
2015/08/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

(三)Nginx配置·续

概述 前文写了关于Nginx环境配置,但是还没有完,接下来将会继续讲三个相关的配置 主要是以下三个 1.Nginx访问日志 2.Nginx日志切割 3.静态文件不记录日志和过期时间 Nginx访问日志 1.先看看...

杉下
今天
1
0
jquery创建类似于java的map

var map = {}; // Map map = new HashMap(); map[key] = value; // map.put(key, value); var value = map[key]; // Object value = map.get(key); var has = key in map; // boolean has = ......

SuperDabai
今天
0
0
java大数据转换16进制转10进制

public static void main(String[] args) {String hex = "0xdbf3accc683297cf0000";BigInteger amount = new BigInteger(hex.substring(2), 16);System.out.println(amount);......

任梁荣
昨天
2
0
OSChina 周六乱弹 —— 目测我们程序员丁克的几率不大

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @真Skr小机灵鬼儿:8.13分享Jocelyn Pook/Russian Red的单曲《Loving Strangers》 《Loving Strangers》- Jocelyn Pook/Russian Red 手机党少...

小小编辑
昨天
13
3
TypeScript基础入门 - 函数 - 剩余参数

转载 TypeScript基础入门 - 函数 - 剩余参数 项目实践仓库 https://github.com/durban89/typescript_demo.gittag: 1.2.1 为了保证后面的学习演示需要安装下ts-node,这样后面的每个操作都能...

durban
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部