【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

时间:2022-03-19 11:22:55

地址: 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。

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

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

  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的头文件目录路径,如下图:

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

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

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

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

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

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

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

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()中,添加这个两个函数,如下图:

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

然后就可以在程序的任何地方调用了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访问:

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持HTTPS

【转】如何在Windows+VS2005使用最新静态libcurl 7.35.0获取网页数据,支持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