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


    地址: 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大笑,下载地址:

    [plain] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    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文件最后面,添加如下代码:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    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的源码如下:

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



    VVCurl.cpp的源码如下:

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



       2.4 编写代码

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

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

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    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 调试

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

    [plain] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. Linking...  
    2. libcurld.lib(asyn-thread.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.pdb' is missing debugging information for referencing module; linking object as if no debug info  
    3. libcurld.lib(base64.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.pdb' is missing debugging information for referencing module; linking object as if no debug info  
    4. libcurld.lib(bundles.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.pdb' is missing debugging information for referencing module; linking object as if no debug info  
    5. libcurld.lib(conncache.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.pdb' is missing debugging information for referencing module; linking object as if no debug info  
    6. libcurld.lib(connect.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.pdb' is missing debugging information for referencing module; linking object as if no debug info  
    7. libcurld.lib(cookie.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.pdb' is missing debugging information for referencing module; linking object as if no debug info  
    8. libcurld.lib(curl_addrinfo.obj) : warning LNK4204: 'c: esourcevccode estlibcurl estlibcurldebugvc80.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  
  • 相关阅读:
    sshpass做秘钥分发,ansible做自动化运维工具
    Day7 面向对象和类的介绍
    R-aggregate()
    R-seq()
    R-ts()
    R-ets()
    python-无联网情况下安装skt-learn
    python-线性回归预测
    python-matplotlib-ERROR
    python-pyhs2
  • 原文地址:https://www.cnblogs.com/cappuccino/p/5444872.html
Copyright © 2020-2023  润新知