zoukankan      html  css  js  c++  java
  • HttpClient 模拟登陆知乎

    最近做爬虫相关工作,我们平时用HttpWebRequest 比较多,每一个Url都要创建一个HttpWebRequest实例,

    而且有些网站验证比较复杂,在登陆及后续抓取数据的时候,每次请求需要把上次的Cookie传递给这次请求。

    记得这篇博客(http://www.cnblogs.com/dudu/archive/2013/03/05/httpclient.html)结尾,dudu总结了:

    HttpClient最与众不同的地方是同一个HttpClient实例可以发出多次请求,每次请求是可以是完全不同的URL。

    而一个HttpWebRequest实例对应于一个Url的一次请求。这才是HttpClient与HttpWebRequest的最大区别所在。

    那么为什么不用HttpClient呢?

    源码地址:https://github.com/zzhi/Spider4Net

    本着学习的目的,那我就拿知乎练习一下,看看HttpClient好用否?

    1,分析登陆页:https://www.zhihu.com/#signin

    根据上图设置 DefaultRequestHeaders

     HttpClient h = new HttpClient(
                new HttpClientHandler
                {
                  //CookieContainer = cookies,
                  AutomaticDecompression = DecompressionMethods.GZip //防止返回的json乱码
                                           | DecompressionMethods.Deflate
                });
                
                h.DefaultRequestHeaders.Add("UserAgent", Configs.ChromeAgent);
                h.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4");
                h.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch");
                h.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
                //1.首页
                var response = await h.GetAsync(index);
                string content = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    //获取隐藏的input值
                    HtmlDocument doc = new HtmlDocument();
                    doc.LoadHtml(content);
                    var xsdf = DocumentHelper.GetInputValue(doc, "_xsrf");//登录需要
                    nameValue["_xsrf"] = xsdf;
                }
                else
                {
                    return null;
                }

    2,分析登陆页:https://www.zhihu.com/login/phone_num(我这里是手机和密码登录):

    分析:除了要设置DefaultRequestHeaders,还需获取_xsrf的值<input type="hidden" name="_xsrf" value="3bc639713d3f8bb899009a7dfa37f9d2"/>,

    这里还需注意:1, Content-Type: application/x-www-form-urlencoded; charset=UTF-8 该如何设置? 参考地址:http://ronaldrosiernet.azurewebsites.net/Blog/2013/12/07/posting_urlencoded_key_values_with_httpclient

                       2, 登录返回的JSON结果是乱码,该如何处理? 参考地址:http://stackoverflow.com/questions/9242472/retrieve-json-data-with-httpclient

    //2.登陆
                h.DefaultRequestHeaders.Clear();
                h.DefaultRequestHeaders.Add("UserAgent", Configs.ChromeAgent);
                h.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest");
                h.DefaultRequestHeaders.Add("Origin", index);
                h.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4");
                h.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch");
                h.DefaultRequestHeaders.Add("Accept", "*/*");
                //post参数
                nameValue["password"] = PassWord;
                nameValue["captcha_type"] = "cn";
                nameValue["remember_me"] = "true";
                nameValue["phone_num"] = Phone;
                StringBuilder sb = new StringBuilder();
                foreach (var key in nameValue.AllKeys)
                {
                    sb.AppendFormat("{0}={1}&", key, nameValue[key]);
                }
                var str = sb.ToString().TrimEnd('&');
                var request = new HttpRequestMessage(HttpMethod.Post, login);
                var requestContent = str;
                request.Content = new StringContent(requestContent, Encoding.UTF8, "application/x-www-form-urlencoded");
    
                response = await h.SendAsync(request);
                content = await response.Content.ReadAsStringAsync();
                var dic = DocumentHelper.JsonToDic(content);
    
                if (dic.ContainsKey("msg"))
                {
                    if (dic["msg"] != "登陆成功")//登录过于频繁,请稍等重试;errcode:100030
                    {
                        Console.WriteLine(dic["msg"]);
                        return null;
                    }
                }

    3. 登录成功后,后面就由大家随便折腾了。这里获取登陆后的首页信息吧。

    //3.抓取首页
                h.DefaultRequestHeaders.Clear();
                h.DefaultRequestHeaders.Add("UserAgent", Configs.ChromeAgent);
                h.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4");
                h.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch");
                h.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
                response = await h.GetAsync(index);
                content = await response.Content.ReadAsStringAsync();

    4,我也不对数据做处理了,看看结果:

    上图是调试状态下的可视化工具视图。

    源码地址:https://github.com/zzhi/Spider4Net, 里面也包含了用HttpWebRequest 方式登录的代码。

    以上就是所有了,如果有时间给大家讲讲登录验证码的识别,但是这个略麻烦,需要根据具体网站的验证码训练一个验证码库,

    复杂的无法识别的验证码就只能用打码兔了,其实也可以自己写个类似打码兔的软件,但需要有人值守,人工识别验证码。

    这这么点东西,昨晚花费了3小时(9-12),今早写博客又花费了1小时。

  • 相关阅读:
    MVCCache2.应用程序缓存(Cache:1.输出缓存[2].应用程序缓存)
    接口和抽象类的区别和作用(功能、用途、好处)
    MVCCache1.输出缓存(Cache:[1].输出缓存2.应用程序缓存)
    JAVA中为什么要使用接口,继承接口不是还要重写方法吗?为什么不直接写那些方法呢?:::接口的最主要的作用是达到统一访问
    枚举的意义,用途,作用,用法,作用场景
    C++ compile multiple cpp and header files via I. cpp1.cpp cpp2.cpp o
    C++retrieve array via returned pointer,traverse the array pointer without the array size
    C++ pass array as pointer and sort in bubble
    C write and read file via FILE and open method in a+ or r mode
    C++ connection mysql and retrieve select sql result
  • 原文地址:https://www.cnblogs.com/zhangzhi19861216/p/5541303.html
Copyright © 2011-2022 走看看