地址: http://blog.csdn.net/hujkay
作者:Jekkay Hu(34538980@qq.com)
关键词:Windows,curl,ssl,  visual c++ 2005, libcurl, https,网页抓取
时间: 2014/2/18

1. 概述

由于Curl提供强大的网络功能,支持HTTP,HTTPS, DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet ,TFTP等,已成为应用最为广泛的轻量级网络库之一。libCurl支持Windows,但如果在Win 平台使用VC开发的话,则需要下载msvc的版本,其下载地址是:http://curl.haxx.se/download/,如:libcurl-7.19.3-win32-ssl-msvc.zip。

目前Curl的的最新版本已经是7.35.0,但是官网提供的msvc的版本仍然是2009年2月发布的7.19.3版本,而且还没有含静态openssl的lib,这就意味写个小exe程序的话,还得打包好几个Openssl DLL进去,挺麻烦的,所以我就重新编译了一个含Openssl静态库,这个库算是我编译的最大的库了,达到25M,下载地址:

  1. 已编译好含SSL的静态libcurl 7.35.0[VC2005].zip
  2. http://download.csdn.net/detail/hujkay/6931345

2. 使用

我以MFC Dialog based工程为例,介绍如何在Windons+VC2005上使用libcurl 7.35.0静态库。

2.1. 创建工程

打开Visual studio 2005,直接创建一个MFC工程,工程类型选择基于对话框[Dialog based]的就行,编码方式取消Unicode,这样就可以使用ANSI编码.

2.2  配置工程属性

右键工程属性,设置Curl的头文件目录路径,如下图:

配置库的链接方式和编码方式,如下图:

配置Runtime library,Debug模式为/MTD,Rlease模式为/MT

然后在Preprocesser里面添加预订义宏CURL_STATICLIB,如下图:

Debug模式和Release模式,配置的内容是一样的。

然后在stdafx.h文件最后面,添加如下代码:

  1. //// 添加CURL库
  2. #include <curl/curl.h>
  3. //// 带SSL的静态链接库
  4. #ifdef _DEBUG
  5. #pragma message("======编译======[DEBUG] CURL库=====")
  6. #pragma comment(lib,"libcurld.lib")
  7. #else
  8. #pragma message("======编译======[Release] CURL库=====")
  9. #pragma comment(lib,"libcurl.lib")
  10. #endif
  11. #pragma comment(lib,"wldap32.lib")
  12. #pragma comment(lib,"ws2_32.lib")

2.3  封装Curl库访问

为了使得Curl访问更加方便,我简单封装了一下Curl的访问类,代码如下:

VVCurl.h的源码如下:

  1. #pragma once
  2. #include <string>
  3. enum CURL_TYPE {CURL_GET=1,CURL_POST=2};
  4. class CVVCurl
  5. {
  6. public:
  7. CVVCurl(void);
  8. ~CVVCurl(void);
  9. BOOL            Init(CString strProxyAddr=_T(""),INT nPort=80) ;
  10. // 释放资源
  11. void            Release();
  12. // 打开指定的网页
  13. BOOL            OpenURL(std::string strURL,CURL_TYPE ntype = CURL_GET);
  14. BOOL            OpenURL(std::string strURL,std::string strPostData,CURL_TYPE ntype = CURL_POST);
  15. // 获取网页内容
  16. const char *    GetHeadContent() { return m_headcontent.c_str() ;} ;
  17. size_t          GetHeadContentLength() { return m_headcontent.size() ;} ;
  18. const char *    GetBodyContent() { return m_bodycontent.c_str() ;} ;
  19. size_t          GetBodyContentLength() { return m_bodycontent.size() ;} ;
  20. protected:
  21. BOOL            InitCurlHandle() ;
  22. BOOL            ReleaseCurlHandle() ;
  23. BOOL            DeleteCookieFile() ;
  24. BOOL            SetCurlHandleOpt() ;
  25. protected:
  26. // 句柄
  27. CURL *              m_pcurl;
  28. // 获取的内容
  29. std::string         m_headcontent ;
  30. std::string         m_bodycontent ;
  31. std::string         m_debugcontent ;
  32. // agent
  33. std::string         m_agent ;
  34. // cookie
  35. std::string         m_cookiepath ;
  36. // proxy
  37. std::string         m_strProxyServer ;
  38. // port
  39. int                 m_nPort ;
  40. };

VVCurl.cpp的源码如下:

  1. #include "StdAfx.h"
  2. #include "VVCurl.h"
  3. //#include "../include/Util.h"
  4. /*
  5. ptr是指向存储数据的指针,
  6. size是每个块的大小,
  7. nmemb是指块的数目,
  8. stream是用户参数。
  9. 所以根据以上这些参数的信息可以知道,ptr中的数据的总长度是size*nmemb
  10. */
  11. static size_t call_wirte_func(const char *ptr, size_t size, size_t nmemb, std::string *stream)
  12. {
  13. size_t len  = size * nmemb;
  14. stream->append(ptr, len);
  15. return len;
  16. }
  17. // 返回http header回调函数
  18. static size_t header_callback(const char  *ptr, size_t size, size_t nmemb, std::string *stream)
  19. {
  20. size_t len  = size * nmemb;
  21. stream->append(ptr, len);
  22. return len;
  23. }
  24. static int debug_callback (CURL * pcurl, curl_infotype ntype, char * ptr, size_t size, std::string  * stream)
  25. {
  26. int len  = (int)size;
  27. stream->append(ptr, len);
  28. return len;
  29. }
  30. CVVCurl::CVVCurl(void)
  31. {
  32. m_pcurl = NULL ;
  33. m_agent = _T("Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER") ;
  34. m_cookiepath = _T("cookie.txt") ; //CUtil::GetRandTempPath(_T("_cookie.txt"));
  35. }
  36. CVVCurl::~CVVCurl(void)
  37. {
  38. Release() ;
  39. }
  40. BOOL CVVCurl::Init(CString strProxyAddr,INT nPort)
  41. {
  42. m_nPort = nPort ;
  43. m_strProxyServer = strProxyAddr ;
  44. return InitCurlHandle() ;
  45. }
  46. void CVVCurl::Release()
  47. {
  48. ReleaseCurlHandle() ;
  49. DeleteCookieFile() ;
  50. }
  51. BOOL CVVCurl::InitCurlHandle()
  52. {
  53. if( NULL == m_pcurl)
  54. {
  55. m_pcurl = curl_easy_init() ;
  56. }
  57. if( NULL == m_pcurl ){
  58. ASSERT(FALSE) ;
  59. return FALSE ;
  60. }
  61. SetCurlHandleOpt() ;
  62. return TRUE ;
  63. }
  64. BOOL CVVCurl::SetCurlHandleOpt()
  65. {
  66. if( NULL == m_pcurl )
  67. return FALSE ;
  68. // 设置Agent
  69. curl_easy_setopt(m_pcurl, CURLOPT_USERAGENT, m_agent.c_str());
  70. // 官方下载的DLL并不支持GZIP,Accept-Encoding:deflate, gzip
  71. curl_easy_setopt(m_pcurl, CURLOPT_ENCODING, "");
  72. //跳过服务器SSL验证,不使用CA证书
  73. curl_easy_setopt(m_pcurl, CURLOPT_SSL_VERIFYPEER, 0L);
  74. //如果不跳过SSL验证,则可指定一个CA证书目录
  75. //curl_easy_setopt(curl, CURLOPT_CAPATH, "this is ca ceat");
  76. //验证服务器端发送的证书,默认是 2(高),1(中),0(禁用)
  77. curl_easy_setopt(m_pcurl, CURLOPT_SSL_VERIFYHOST, 0L);
  78. /* 与服务器通信交互cookie,默认在内存中,可以是不存在磁盘中的文件或留空 */
  79. curl_easy_setopt(m_pcurl, CURLOPT_COOKIEFILE, m_cookiepath.c_str());
  80. /* 与多个CURL或浏览器交互cookie,会在释放内存后写入磁盘文件 */
  81. curl_easy_setopt(m_pcurl, CURLOPT_COOKIEJAR, m_cookiepath.c_str()) ;
  82. //设置重定向的最大次数
  83. curl_easy_setopt(m_pcurl, CURLOPT_MAXREDIRS, 5);
  84. // 设置自动设置refer字段
  85. curl_easy_setopt ( m_pcurl, CURLOPT_AUTOREFERER, 1 );
  86. //设置301、302跳转跟随location
  87. curl_easy_setopt(m_pcurl, CURLOPT_FOLLOWLOCATION, 1);
  88. //抓取内容后,回调函数
  89. curl_easy_setopt(m_pcurl, CURLOPT_WRITEFUNCTION, call_wirte_func);
  90. curl_easy_setopt(m_pcurl, CURLOPT_WRITEDATA, &m_bodycontent );
  91. //抓取头信息,回调函数
  92. curl_easy_setopt(m_pcurl, CURLOPT_HEADERFUNCTION, header_callback );
  93. curl_easy_setopt(m_pcurl, CURLOPT_HEADERDATA, &m_headcontent);
  94. // 设置超时时间
  95. curl_easy_setopt(m_pcurl, CURLOPT_TIMEOUT, 10);
  96. // 禁用掉alarm这种超时
  97. curl_easy_setopt(m_pcurl, CURLOPT_NOSIGNAL, 1L);
  98. // 禁止重用TCP连接
  99. curl_easy_setopt(m_pcurl, CURLOPT_FORBID_REUSE, 1);
  100. // 打开调试
  101. curl_easy_setopt(m_pcurl, CURLOPT_VERBOSE, 1);
  102. curl_easy_setopt(m_pcurl, CURLOPT_DEBUGFUNCTION, debug_callback);
  103. curl_easy_setopt(m_pcurl, CURLOPT_DEBUGDATA, &m_debugcontent);
  104. // 判断是否是需要代理
  105. if( m_strProxyServer.size() > 0 && m_nPort > 0)
  106. {
  107. // 打开,允许重用TCP连接
  108. curl_easy_setopt(m_pcurl, CURLOPT_FORBID_REUSE, 0);
  109. // 第一种方法
  110. curl_easy_setopt(m_pcurl,CURLOPT_PROXY,m_strProxyServer.c_str());
  111. curl_easy_setopt(m_pcurl, CURLOPT_PROXYPORT, m_nPort);
  112. curl_easy_setopt(m_pcurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  113. curl_easy_setopt(m_pcurl, CURLOPT_HTTPPROXYTUNNEL, 1L);
  114. }
  115. return TRUE ;
  116. }
  117. BOOL CVVCurl::ReleaseCurlHandle()
  118. {
  119. if( NULL != m_pcurl)
  120. {
  121. curl_easy_cleanup(m_pcurl);
  122. }
  123. m_pcurl = NULL ;
  124. return TRUE ;
  125. }
  126. BOOL CVVCurl::DeleteCookieFile()
  127. {
  128. ::DeleteFile(m_cookiepath.c_str());
  129. return TRUE ;
  130. }
  131. BOOL CVVCurl::OpenURL(std::string strURL,CURL_TYPE ntype)
  132. {
  133. return OpenURL(strURL,_T(""),ntype) ;
  134. }
  135. BOOL CVVCurl::OpenURL(std::string strURL,std::string strPostData,CURL_TYPE ntype)
  136. {
  137. m_headcontent = m_bodycontent = m_debugcontent = _T("");
  138. if( NULL == m_pcurl )
  139. {
  140. ASSERT(FALSE) ;
  141. return FALSE ;
  142. }
  143. if( ntype == CURL_POST || strPostData.size() > 0)
  144. {
  145. //curl_easy_setopt(m_pcurl,CURLOPT_HTTPGET,0);
  146. /* POST 数据 */
  147. curl_easy_setopt(m_pcurl,CURLOPT_POST,1);
  148. if(strPostData.size() > 0)
  149. {
  150. curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDS, strPostData.c_str());
  151. curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDSIZE, strPostData.size());
  152. }
  153. else
  154. {
  155. curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDS, NULL);
  156. curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDSIZE, 0);
  157. }
  158. //SetCurlHandleOpt() ;
  159. }else
  160. {
  161. // 禁用POST,直接GET请求
  162. //curl_easy_setopt(m_pcurl,CURLOPT_POST,0);
  163. //curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDS, NULL);
  164. curl_easy_setopt(m_pcurl,CURLOPT_HTTPGET,1);
  165. //SetCurlHandleOpt() ;
  166. }
  167. try
  168. {
  169. // 远程URL,支持 http, https, ftp
  170. curl_easy_setopt(m_pcurl, CURLOPT_URL, strURL.c_str());
  171. CURLcode nRet =  curl_easy_perform(m_pcurl);
  172. return CURLE_OK == nRet ;
  173. }
  174. catch (...)
  175. {
  176. }
  177. return FALSE ;
  178. }

2.4 编写代码

在使用CVVCurl封装类之前必须先调用函数cur_global_init进行全局初始化,再关闭时在调用函数curl_global_cleanup扫尾。我们可以在函数CTestlibCurlApp::InitInstance()中,添加这个两个函数,如下图:

然后就可以在程序的任何地方调用了CVVCurl类来访问网页了,比如我在一个函数响应出使用如下代码获取网页数据:

  1. void CTestlibCurlDlg::OnBnClickedVisitButton()
  2. {
  3. UpdateData(TRUE);
  4. m_Url = m_Url.Trim();
  5. if( m_Url.GetLength() <= 0)
  6. return ;
  7. CVVCurl vvcurl ;
  8. vvcurl.Init() ;
  9. if( vvcurl.OpenURL(m_Url.GetBuffer()))
  10. {
  11. m_ContentEdit.Clear() ;
  12. m_ContentEdit.SetSel(0,-1,FALSE);
  13. m_ContentEdit.ReplaceSel(vvcurl.GetBodyContent(),FALSE) ;
  14. }
  15. }

2.5 调试

编译程序,可能会有许多没有调试符号警告,这个是无所谓的。

  1. Linking...
  2. libcurld.lib(asyn-thread.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info
  3. libcurld.lib(base64.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info
  4. libcurld.lib(bundles.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info
  5. libcurld.lib(conncache.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info
  6. libcurld.lib(connect.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info
  7. libcurld.lib(cookie.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info
  8. libcurld.lib(curl_addrinfo.obj) : warning LNK4204: 'c:\resource\vccode\testlibcurl\testlibcurl\debug\vc80.pdb' is missing debugging information for referencing module; linking object as if no debug info

执行程序结果如下,测试HTTP访问和HTTPS访问:

3. 总结

我封装的CVVCurl访问类是可以支持HTTPS POST的,具体的请看下访问接口就可以了,此外还可以指定Cookie文件 ,是线程安全的封装类。如果需要支持多个账号同时登陆Web,那么只需要为每个不同的账号指定不同的Cookie文件就可以了。

对于抓取的网页内容,如果用的UTF8编码的网页内容可能需要进行编码转换一下,才能正确显示中文,工程中含有代码转换的类CStringConvert,已经加到工程代码中,可直接使用,如果还不懂的话,就请打发一杯咖啡钱给我,让老衲细细道来。【点此打发咖啡】[https://me.alipay.com/jekkay]

以上的测试工程代码,可以在下面网址中下载:

    1. :   VC2005使用含SSL的静态libcurl库代码工程
    2. :  http://download.csdn.net/detail/hujkay/6932541

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS的更多相关文章

  1. 如何在WINDOWS下编译BOOST C++库 .

    如何在WINDOWS下编译BOOST C++库 cheungmine 2008-6-25   写出来,怕自己以后忘记了,也为初学者参考.使用VC8.0和boost1.35.0.   1)下载boost ...

  2. 如何在Windows平台使用VS搭建C++/Lua的开发环境

    转自:http://ju.outofmemory.cn/entry/95358 本文主要介绍如何在Windows平台利用VS搭建C++/Lua开发环境.这里的“C++/Lua开发环境”主要指的是C++ ...

  3. (转)如何在Windows上安装多个MySQL

    原文:http://www.blogjava.net/hongjunli/archive/2009/03/01/257216.html 如何在Windows上安装多个MySQL 本文以免安装版的mys ...

  4. 如何在windows下安装GIT

    如何在windows下安装GIT 分步阅读 Git是一个免费的.开源的版本控制软件.在Windows上安装git,一般为msysgit,官方下载地址为 http://code.google.com/p ...

  5. 如何在Windows中打开多个Windows Media Player

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:如何在Windows中打开多个Windows Media Player.

  6. Redis简介以及如何在Windows上安装Redis

    Redis简介 Redis是一个速度非常快的非关系型内存数据库. Redis提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erla ...

  7. 如何在Windows 10上运行Docker和Kubernetes?

    如何在Windows 10上运行Docker和Kubernetes? 在Windows上学习Docker和Kubernetes,开始的时候会让你觉得无从下手.最起码安装好这些软件都不是一件容易的事情. ...

  8. Redis进阶实践之三如何在Windows系统上安装安装Redis(转载)

    Redis进阶实践之三如何在Windows系统上安装安装Redis 一.Redis的简介 Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括 ...

  9. 如何在 Windows 10 中搭建 Node.js 环境?

    [编者按]本文作者为 Szabolcs Kurdi,主要通过生动的实例介绍如何在 Windows 10 中搭建 Node.js 环境.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 在本文中 ...

随机推荐

  1. 13 Balls Problem

    今天讨论的是称球问题. No.3 13 balls problem You are given 13 balls. The odd ball may be either heavier or ligh ...

  2. crm 2013邮箱设置 “允许使用凭据进行电子邮件处理” 被禁用的解决

    记录一下: 在CRM 2013/2015的邮箱设置时发现“允许使用凭据进行电子邮件处理” 选项被禁用而且无法输入凭证(如下图): 查阅官方说明得知考虑邮件安全性只能在IFD部署或https访问模式下才 ...

  3. C#与Java的比较

    C#与Java的比较 写完后得知维基百科里有更加全面得多的比较: http://en.wikipedia.org/wiki/Comparison_of_C_Sharp_and_Java NET(C#) ...

  4. Runtime初学习

    什么是runtime? runtime是一套纯c的API.平时用oc写的代码在运行时都会先转成runtime代码,然后在执行. runtime可以干什么? 1.交换方法.(method_exchang ...

  5. Struts2 的 值栈和ActionContext

    1.ValueStack 和 ActionContext 的关系与区别: -- 相同点:它们都是在一次HTTP请求的范围内使用的,它们的生命周期都是一次请求 -- 不同点:ValueStack 分为对 ...

  6. nullcon HackIM 2016 -- Crypto Question 4

    He is influential, he is powerful. He is your next contact you can get you out of this situation. Yo ...

  7. Huffman编码

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstri ...

  8. Spring环境搭建之:导入jar包、配置文件名称及放置位置

    Spring环境搭建之:导入jar包.配置文件名称及放置位置 现在项目开发中spring框架应用的还是比较多的,自己用的还不太熟练,每次用的时候总配置半天,总有些配置弄错,就找个时间总结以下,方便以后 ...

  9. UVA 10054 (欧拉回路) The Necklace

    题目:这里 题意:有一种由彩色珠子连接而成的项链,每个珠子两半由不同颜色(由1到50的数字表示颜色)组成,相邻的两个珠子在接触的地方颜色相同,现在有一些零碎的珠子,确认它是否能 复原成完整的项链. 把 ...

  10. 统一资源定位符URL类

    package j2se.core.net.base; import java.io.BufferedReader;import java.io.IOException;import java.io. ...