libcurl实现http登录功能

2023-05-16

Fiddler Web Debugger捕捉http数据包:

       观察看看,POST请求的地址为http://passport.cnblogs.com/login.aspx?ReturnUrl=http%3a%2f%2fwww.cnblogs.com%2f,所有的请求数据都将发往login.aspx这个页面。Referer字段是指从哪个页面跳向这个页面的,一般用于反盗链。我们模拟Http请求的时候,把它原样复制进去就是。User-Agent则表明使用的浏览器内核版本信息,这里我用的是IE9。在模拟的时候也招办不误。剩余字段中最重要的是Host和Accept-Encoding两个字段。其中Accept-Encoding表明客户浏览器能接受什么格式的数据,gzip表示浏览器可接受压缩格式的数据。这在编写客户端的时候需要注意了,因为浏览器可以对gzip格式数据解码,除非自己实现解码功能,否则我们的客户端还是用deflate格式。这里的Cookie不知道是干什么用的,不过在登录之前我想对用户作用不大。

       这里用的是POST请求方式,报文数据部分才是登录时最需要的数据。Fiddler的功能真是强大,看看下图就知道了:

       可以看到,POST发送的数据总共有8对。其中__EVENTTARGET和__EVENTARGUMENT字段目前是空的,__VIEWSTATE和__EVENTVALIDATION则是两个很长的字符串,具体作用不知道,但是这不影响我们。在验证的时候我们手动组装即可,自动登录的时候从页面中过滤出来即可。后面将利用htmlcxx这个工具完成。剩下四个字段中只有用户名和密码是变化的,其他两个字段固定不变,拼接到末尾即可。也就是说,我们需要自己组装http报文头部和数据部分。这个工作利用Libcurl这个库来完成。

模拟HTTP请求

       那么接下来的工作就是组装Http数据包了。libcurl是完成这项工作的有力工具,关于这个工具的使用网上的页面挺多,但是正式用在模拟登陆中的少见。这篇博文倒是讲解了利用libcurl登陆csdn的原理。然而区别的是,该博文中并未讲解如何使用POST方式请求数据。因此在摸索过程遇到不少困难,接下来以代码的形式讲解组包发送的过程:

void createSession(CURL* curl, int postoff, const char* post_params, const char* post_url, const char* hosts, const char* refer, struct curl_slist *headers){    if(curl){        headers = curl_slist_append(headers,"User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)");        headers = curl_slist_append(headers, hosts);        headers = curl_slist_append(headers,"Accept: text/html, application/xhtml+xml, */*");        headers = curl_slist_append(headers,"Accept-Language:zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");        headers = curl_slist_append(headers,"Accept-Encoding:deflate");        headers = curl_slist_append(headers, refer);        headers = curl_slist_append(headers,"Connection:keep-alive");                 curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookie.txt");        //把服务器发过来的cookie保存到cookie.txt        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);    curl_easy_setopt(curl, CURLOPT_URL, post_url);     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params);        // 使用POST方式发送请求数据     curl_easy_setopt(curl, CURLOPT_POST, postoff);     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);         curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookie.txt");       // cookies文件    } }

  在调用该函数先需要先初始化libcurl的上下文环境,并将初始化得到的CURL*指针传递进来。注意headers是一个struct curl_slist*类型的指针,在使用之前需要先清空。这里需要注意的是:每一次发送请求数据之前,我们都要清空这个headers所指向的结构体,否则会服务器会返回400错误!在上面的函数中,我们初始化了headers结构体。这个结构体存储的都是数据包头部相关的字段,前面抓取到的字段全部往这里面塞就行了。curl_easy_setopt()函数是libcurl中非常重要的函数,其功能类似于fnctl和ioctl这样的系统调用,主要用于控制libcurl的行为。这里需要需要注意的是CURLOPT_POSTFIELDS这个属性,它用于控制当前的请求方式是否使用POST。

​​​​​​​int loginServer(){ CURL* curl = NULL; CURLcode res = CURLE_FAILED_INIT; const char* filename = "out.txt"; struct curl_slist *headers = NULL; FILE* outfile; static const char* post_params = "__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=(前面的内容)&__EVENTVALIDATION=(前面的内容)&tbUserName=name&tbPassword=name&btnLogin=%E7%99%BB++%E5%BD%95&txtReturnUrl=http%3A%2F%2Fwww.cnblogs.com%2F"; static const char* post_url = "http://passport.cnblogs.com/login.aspx?ReturnUrl=http%3a%2f%2fwww.cnblogs.com%2f"; static const char* refer = "Referer: http://passport.cnblogs.com/login.aspx?ReturnUrl=http%3A%2F%2Fwww.cnblogs.com%2F"; curl_global_init(CURL_GLOBAL_ALL);    curl = curl_easy_init(); createSession(curl, 1, post_params, post_url, "Host:passport.cnblogs.com", refer, headers); outfile = fopen(filename, "w"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 注册回调函数,当数据到来的时候自动调用这个函数存储数据 curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); // 和回调函数一起设置,表示数据存储的地方    //执行http请求    res = curl_easy_perform(curl); // 发送数据、接受数据等工作,我们不需插手    //释放资源    curl_easy_cleanup(curl);    curl_slist_free_all(headers);     curl_global_cleanup(); fclose(outfile); return res == CURLE_OK; }

  接着便是登录了。我们首先手动组装了需要发送的数据部分,这个地方也需要注意:如果是直接从网页中提取出来的话,需要进行编码将' ', '/', '+'等字符编码替换。这里是手动的直接粘贴即可。然后就初始化libcurl的使用环境,设置回调函数保存数据。curl_easy_perform()在后台完成了所有的工作,数据的首发、cookies文件的发送保存工作都不要程序员插手。所以整个代码看起来非常简单。

      调用完成后将在工程目录下可以看到下载到的页面源代码。如果登录成功,还可以在工程目录下可到生成的cookies文件,而从服务器返回的数据内容如下:

      接下来我们就可以开始访问我们账户的数据了,如我评论过的博文、我推荐过的博文、我关注的人!那么,我们还得先把页面代码下载下来:

void downloadPage(){    CURLcode res = CURLE_FAILED_INIT;    CURL* curl = NULL;    FILE* homepage;    struct curl_slist *headers = NULL;    static const char* post_url = "http://www.cnblogs.com/aggsite/mydigged";    // 我推荐过的博文    static const char* refer = "Referer: http://www.cnblogs.com/login.aspx?ReturnUrl=http%3A%2F%2Fwww.cnblogs.com%2F";     if (loginServer())    {        curl_global_init(CURL_GLOBAL_ALL);        curl = curl_easy_init();        createSession(curl, 0, "", post_url, "Host:www.cnblogs.com", refer, headers);        homepage = fopen("homepage.txt", "w");        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);         curl_easy_setopt(curl, CURLOPT_WRITEDATA, homepage);        //执行http请求        res = curl_easy_perform(curl);         //释放资源        curl_easy_cleanup(curl);        curl_slist_free_all(headers);        curl_global_cleanup();        fclose(homepage);    }}

  请求URL设置为http://www.cnblogs.com/aggsite/mydigged,表示我推荐过的博文页面。而Referer和host字段则根据fiddler抓取结果进行填充。注意这里的headers又进行了一次初始化哦。其他的仍然保持不变。要是没有什么大问题,这个页面的源代码已经下载完成了。那么接下来的工作就是解析页面内容了。

解析页面内容

      解析HTML这种结构性文本用字符串查找的方式或正则表达式看似都行,但是工作量实在太大,准确性还很难说。在网上找到一个专用于解析html代码的C++库:htmlcxx。这个库是C++编写的,目前似乎已经停止更新了,最新的版本下载到的是0.84。这个库下载下来的是源代码,需要进行编译生成lib使用。在windows环境下我使用vs2010直接编译的,没有错误产生。这个库的文档基本没有,网上只有少数的几个例子。下面以实例讲解下该库的使用方式:

using namespace htmlcxx;<br>fstream out;out.open("out.txt", ios::out);     // 所有的解析结果全部保存在out.txt文件中fstream htmlFileStream;htmlFileStream.open( "test.txt", ios::in );    // text.txt中保存的是上文中下载的页面源代码istreambuf_iterator<char> fileBeg(htmlFileStream), fileEnd;string html( fileBeg, fileEnd );htmlFileStream.close(); HTML::ParserDom parser;tree<HTML::Node> dom = parser.parseTree(html); tree<HTML::Node>::iterator domBeg = dom.begin();tree<HTML::Node>::iterator domEnd = dom.end();

    先引入命名空间初始化解析器,并从中获取到两个迭代器。该库允许我们以迭代器的方式来遍历其构造的DOM树:

int count;string temp;for (; domBeg != domEnd; ++domBeg)   // 遍历文档中所有的元素{    if (!domBeg->tagName().compare("div"))    // 查找所有div标签    {        domBeg->parseAttributes();    // 这个函数很重要。如果不调用,我们无法获取标签的属性。而下面我们正需要获取div的class属性,所以必须调用。        if (!domBeg->attribute("class").second.compare("post_item"))   // 如果是class属性值为post_item,表明是一个博文结构,开始解析        {            count = 0;  // count计数,每条博文只解析7个字段,主要是为了跳出循环。没有找到更好的跳出循环的方法            out << "-----------------------------------------------" << endl;            for (; domBeg != domEnd; ++domBeg)            {                if (!domBeg->tagName().compare("a"))  // 如果是a标签,则将a标签的href属性值提取出来保存到文件                {                    domBeg->parseAttributes();                    out << domBeg->attribute("href").second << endl;                }                if (!domBeg->isTag())   // 如果不是html标签而是普通文本,那么就要进行空格处理                {                    temp = domBeg->text();  // 先将该文本提出取出来                    temp.erase(0,temp.find_first_not_of(" \t\v\r\n"));  // 去掉' ', '\t', '\v', '\n', '\r'                    temp.erase(temp.find_last_not_of(" \t\v\r\n") + 1);                    if (!temp.empty())  // 如果剔除了空格字符之后还剩下其他字符,则保存到文件                    {                        out << temp << endl;                        ++count;                    }                }                if (count == 7)   // 已经找到7个字段,跳出循环,继续下一条博文的解析                {                    break;                }            }        }    } }

    上面的注释已经非常清楚了,htmlcxx这个库的使用也非常简单,提供的API只有七八个。看看都输出了些什么:

       结果还不错,代码量却很少。还真的是挺强大的,算法的力量!要是光靠字符串匹配还正不知道有没有勇气去做。另外,前面还提到了在登录时需要组装POST数据的问题。如果是手动写死在代码中,在推广使用的时候显然是不行的。还得从页面中自动提取才行:

int count = 0;for (; domBeg != domEnd; ++domBeg){    if (!domBeg->tagName().compare("input"))   // 只检查input标签,因为那几个字段都是在input里面    {        domBeg->parseAttributes();        out << "name: " << domBeg->attribute("name").second ;  // 提取键名,即input的name属性        out << " value:" << domBeg->attribute("value").second << endl;  // 提取键值,即input的value属性        if (++count == 4)  // 只要四个字段,提前结束解析工作。        {            break;        }    }}

  再看看提取结果:

      规规矩矩、整整齐齐。好了,htmlcxx的演示到这里结束了。

遇到的问题

  1. htmlcxx在解析中文的时候,可能会出现问题,需要进行调整。网上的代码很多。据说是htmlcxx的一个Bug。

  2. libcurl使用POST的方式。CURLOPT_POSTFIELDS字段。

  3. htmlcxx的编译方式,需要保证编译方式和目标工程方式一直,否则无法和其他库一起配合使用。解决方案:项目属性-->C/C++-->代码生成-->运行库,与目标工程保持一致

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

libcurl实现http登录功能 的相关文章

  • 串口扫盲十一:RS-232至RS-485RS-422接口的智能转换器

    摘要 详细地介绍了如何从RS 232信号线上高效率地产生电源 如何实现RS 232接口与RS 485RS 422接口的智能转换 同时 也给出了具体的硬件设计及软件设计方法 关键词 RS 232 RS 485RS 422 接口 智能转换器 随
  • strcpy()/strcat()

    为什么80 的码农都做不了架构师 xff1f gt gt gt char p1 15 61 34 abcd 34 p2 61 34 ABCD 34 str 50 61 34 xyz 34 strcpy str 43 2 strcat p1
  • 路由器逻辑接口配置总结

    逻辑接口配置 1 Loopback接口配置 Loopback 回环 接口是完全软件模拟的路由器本地接口 xff0c 它永远都处于UP状态 发往Loopback接口的数据包将会在路由器本地处理 xff0c 包括路由信息 Loopback接口的
  • 联合体、结构体简析

    1 联合体 结构体定义 联合体 xff1a 在进行某些算法的C语言编程的时候 xff0c 需要使几种不同类型的变量存放到同一段内存单元中 也就是使用覆盖技术 xff0c 几个变量互相覆盖 这种几个不同的变量共同占用一段内存的结构 xff0c
  • tcpdump抓包命令

    该命令是抓包分析工具 xff0c 可以将数据包的头或者是整个包抓取下来进行分析 xff0c 支持针对特定协议 主机进行过滤 xff0c 同时支持逻辑操作 抓取本机第一个网络接口通常是eth0上所有的包 tcpdump 抓取指定指定网卡上的的
  • rundll32.exe命令使用大法

    lt DOCTYPE html PUBLIC WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml strictdtd gt Rundll32 exe是什么 xff1f 顾名思意 xff0c 执行3
  • sftp

    引述自 xff1a http cs ecust edu cn snwei studypc oftencommand ftp htm sftp的命令格式为 xff1a sftp v d i n g hostname v 显示远程服务器的所有响
  • L298N接线图

    转载于 https my oschina net surenpi blog 481745
  • 大数据:Hive - ORC 文件存储格式

    一 ORC File文件结构 ORC的全称是 Optimized Row Columnar xff0c ORC文件格式是一种Hadoop生态圈中的列式存储格式 xff0c 它的产生早在2013年初 xff0c 最初产生自Apache Hiv
  • WPF RichTextBox 禁止换行

    原文 WPF RichTextBox 禁止换行 这个问题困扰了好久 xff0c 进过不断的努力 xff0c 终于解决了 lt RichTextBox Margin 61 34 0 44 10 0 34 Name 61 34 codeText
  • 浅谈微信小程序用setStorage和getStorage缓存和获取数据

    缓存数据 每个微信小程序都可以有自己的本地缓存 xff0c 可以通过 wx setStorage xff08 wx setStorageSync xff09 wx getStorage xff08 wx getStorageSync xff
  • 微信扫码登录是如何实现的?

    网页版微信刚推出时 xff0c 无数人被它的登录方式惊艳了一下 xff0c 不需要输入用户名密码 xff0c 打开手机微信扫一扫 xff0c 便自动登录 从原理上讲 xff0c 二维码只能是一段文本的编码 xff0c 如何用它实现快捷登录的
  • python - http请求带Authorization

    背景 接入公司的一个数据统计平台 xff0c 该平台的接口是带上了Authorization验证方式来保证验签计算安全 方法 其实很简单 xff0c 就是在header中加入key 61 Authorization xff0c value是
  • element设置设置日期0点到23:59:59

    2019独角兽企业重金招聘Python工程师标准 gt gt gt js方式 var start 61 new Date new Date new Date toLocaleDateString getTime 当前日期0点到23 xff1
  • 一张图看懂亮度、明度、光度、光亮度、明亮度

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 亮度 明度 光亮度 xff0c Luminance和Brightness lightness其实都是一个意思 xff0c 只是起名字太难了 提出一个颜色模型后 xff0c
  • prfpset文件怎么导入pr?pr预设如何导入?

    prfpset文件是一种可以导入到Adobe Premiere Pro中使用的预设文件 xff0c 导入后即可为你的画面进行调色 xff0c 不同的pr预设拥有不同的调色效果 xff0c 那么prfpset格式预设怎么打开 xff1f pr
  • How does java technology relate to cloud computing?

    Java Paas shootout 64 IBM developer Cloud computing is always a hot topic around IT field today How can it bring to us H
  • LCD1602A接线图

    转载于 https my oschina net surenpi blog 481697
  • 3dmax2020卸载/安装失败/如何彻底卸载清除干净3dmax2020注册表和文件的方法

    3dmax2020提示安装未完成 xff0c 某些产品无法安装该怎样解决呢 xff1f 一些朋友在win7或者win10系统下安装3dmax2020失败提示3dmax2020安装未完成 xff0c 某些产品无法安装 xff0c 也有时候想重
  • matlab练习程序(生成黑白网格)

    提供了两种生成方法 xff0c 一个是自己编程实现 xff0c 比较灵活 xff1b 另一个是调用系统的checkerboard函数 xff0c 似乎只能生成8 8网格 至于用途 xff0c 也许可以用来下国际象棋 自己函数生成 xff1a

随机推荐

  • git 基本操作

    配置 user name user email git config global user name 34 fatdai 34 git config global user email 34 1012607376 64 qq com 34
  • 第五篇:明确拒绝不想编译器自动生成的拷贝构造函数和赋值运算符重载函数...

    前言 如果你不想要编译器帮你自动生成的拷贝机制 参考前文 xff0c 那么你应当明确的拒绝 如何拒绝 xff1f 这便是本文要解决的主要问题 问题描述 当你定义了一个类 xff0c 而这个类中各对象之间也是封装的 禁止同类对象之间的相互赋值
  • springboot开启access_log日志输出

    由于在调试时需要查看access log日志 xff0c 但是springboot默认并没有开启 xff0c 因此查看了一下文档 xff0c 在springboot的配置文件中添加如下设置 xff0c 即可将日志输出当磁盘文件中以供查看 日
  • 向论文作者要代码的邮件怎么写

    Reference 找人要代码的邮件怎么写
  • 当 better-scroll 遇见 Vue

    作者 xff1a 滴滴公共前端 黄轶 在我们日常的移动端项目开发中 xff0c 处理滚动列表是再常见不过的需求了 以滴滴为例 xff0c 可以是这样竖向滚动的列表 xff0c 如图所示 xff1a 竖向列表 也可以是横向滚动的导航栏 xff
  • elementUI中form表单的upload上传图片及校验总结

    自定义校验方法 因为我的项目是 分情况 可以选择是否有图 所以我定义了一个变量hasFmt 当他为false的时候 才会要求上传 走这个校验方法 rules里声明这个方法 var valiIcon 61 rule value callbac
  • iview Table表格组件无法拆分单元格的解决思路

    最近在开发的Vue项目中 xff0c 使用了iview第三方UI库 对于表格组件的需求是最多的 xff0c 但是在一些特定场景下 xff0c 发现iview的表格组件没有单元格合并与拆分的API xff0c 搜了一下发现很多同学提问关于iv
  • Django+Semantic-ui建立Blog网站(一、基本框架)

    Django应该算是最流行的web应用框架了吧 xff0c 而语义化的CSS框架semantic ui我觉得非常好用 xff0c 使用这个组合做一个blog站点 关于Django的详细介绍以及MTV模式的相关有优点 xff0c 请翻阅官方文
  • Eclipse插件开发首选项篇

    介绍 如果你的插件需要保存一些数据 xff08 参数 xff09 xff0c 比如要设置一些ip地址等等 这时候 xff0c 就要用到Eclipse提供的首选项这个扩展点 我们这里的首选项的数据类型只包括Java中的基本数据类型 扩展点 o
  • java 判断字符串是否是json格式

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 两种实现方式 xff1a 1 通过fastjson解析来判断 xff0c 解析成功 xff0c 是json格式 xff1b 否则 xff0c 不是json格式 xff1a
  • Error response from daemon: Error processing tar file(exit status 1): remove /hosts: device or resou

    2019独角兽企业重金招聘Python工程师标准 gt gt gt You cannot copy over etc hosts Docker provides the container with a custom etc hosts f
  • Jquery动态添加标签元素,在指定标签前或者标签后(append/prepend用法)

    Jquery动态添加标签元素 在指定标签前或者标签后 xff08 append prepend用法 xff09 1 append 方法在被选元素的结尾插入指定内容 2 appendTo 方法在被选元素的结尾插入 HTML 元素 3 prep
  • ASP.NET中常用正则表达式

    34 d 43 34 非负整数 xff08 正整数 43 0 xff09 34 0 9 1 9 0 9 34 正整数 34 d 43 0 43 34 非正整数 xff08 负整数 43 0 xff09 34 0 9 1 9 0 9 34 负
  • 总结 IOS 7 内存管理

    iOS7的一些总结 5 iOS中的内存管理 我们知道 xff0c 为了更加方便地处理内存管理问题 xff0c 将开发人员从繁琐的内存的分配和释放工作中解放出来而专注于产品和逻辑 xff0c iOS提供了一种有效的方法 xff0c 即自动引用
  • 《STL源码剖析》---list容器insert操作的个人理解

    最近在看STL源码剖析 xff0c 感觉还是挺深奥的 xff0c 感觉看不太懂 今天在看list容器这块 xff0c 讲到了insert操作 xff0c 便记录一番自己的理解吧 摘抄书上的 xff1a iterator insert ite
  • PROCESS_YIELD()宏和C语言的switch语句< contiki学习笔记之七>

    写在前面 xff1a 按照main 函数的代码一行一行的分析 xff0c 该是看到了 etimer process 这个位置 但是etimer process实现里的一个宏 PROCESS YIELD 引出了很多故事 xff0c 于是单独把
  • 用c语言指针实现vector,C使用指针将对象添加到Vector中

    我有一个向量添加包含 SDL Surface 指针作为数据成员的对象 xff0c 这意味着我必须使用复制构造函数来实现指针的深层复制 该对象释放析构函数中的表面 指针 xff0c 这就是问题发生的地方 当对象被添加到向量中时 通过按下按钮
  • 【Http认证方式】——Basic认证

    访问请求 xff1a http 192 168 2 113 8080 geoserver rest workspaces时 xff0c 浏览器弹出窗口需要输入用户名和密码 xff0c 并且 xff0c 如果不输入或者输入错误 xff0c 浏
  • c++ http请求

    平常我们要访问某个URL一般都是通过浏览器进行 xff1a 提交一个URL请求后 xff0c 浏览器将请求发向目标服务器或者代理服务器 xff0c 目标服务器或者代理服务器返回我们所需要的数据 xff0c 浏览器接收到这些数据后保存成文件并
  • libcurl实现http登录功能

    用Fiddler Web Debugger捕捉http数据包 xff1a 观察看看 xff0c POST请求的地址为http passport cnblogs com login aspx ReturnUrl 61 http 3a 2f 2