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

已编译好含SSL的静态libcurl 7.35.0[VC2005].zip
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文件最后面,添加如下代码:

 添加CURL库
#include <curl/curl.h>
 带SSL的静态链接库
#ifdef _DEBUG
#pragma message("======编译======[DEBUG] CURL库=====")
#pragma comment(lib,"libcurld.lib")
#else
#pragma message("======编译======[Release] CURL库=====")
#pragma comment(lib,"libcurl.lib")
#endif
#pragma comment(lib,"wldap32.lib")
#pragma comment(lib,"ws2_32.lib")

   2.3  封装Curl库访问

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

VVCurl.h的源码如下:

#pragma once
#include <string>

enum CURL_TYPE {CURL_GET=1,CURL_POST=2};

class CVVCurl
{
public:
	CVVCurl(void);
	~CVVCurl(void);

	BOOL			Init(CString strProxyAddr=_T(""),INT nPort=80) ;
	// 释放资源
	void			Release();
	// 打开指定的网页
	BOOL			OpenURL(std::string strURL,CURL_TYPE ntype = CURL_GET);
	BOOL			OpenURL(std::string strURL,std::string strPostData,CURL_TYPE ntype = CURL_POST);
	// 获取网页内容
	const char *	GetHeadContent() { return m_headcontent.c_str() ;} ;
	size_t			GetHeadContentLength() { return m_headcontent.size() ;} ;
	const char *	GetBodyContent() { return m_bodycontent.c_str() ;} ;
	size_t			GetBodyContentLength() { return m_bodycontent.size() ;} ;

protected:
	BOOL			InitCurlHandle() ;
	BOOL			ReleaseCurlHandle() ;
	BOOL			DeleteCookieFile() ;
	BOOL			SetCurlHandleOpt() ;

protected:
	// 句柄
	CURL *				m_pcurl;
	// 获取的内容
	std::string			m_headcontent ;
	std::string			m_bodycontent ;
	std::string			m_debugcontent ;
	// agent
	std::string			m_agent ;
	// cookie
	std::string			m_cookiepath ;
	// proxy
	std::string			m_strProxyServer ;
	// port
	int					m_nPort ;


};


VVCurl.cpp的源码如下:

#include "StdAfx.h"
#include "VVCurl.h"
//#include "../include/Util.h"



/*
ptr是指向存储数据的指针,
size是每个块的大小,
nmemb是指块的数目,
stream是用户参数。
所以根据以上这些参数的信息可以知道,ptr中的数据的总长度是size*nmemb
*/
static size_t call_wirte_func(const char *ptr, size_t size, size_t nmemb, std::string *stream)
{
	size_t len  = size * nmemb;
	stream->append(ptr, len);
	return len;
}
// 返回http header回调函数 
static size_t header_callback(const char  *ptr, size_t size, size_t nmemb, std::string *stream)  
{  
	size_t len  = size * nmemb;
	stream->append(ptr, len);
	return len;
}  


static int debug_callback (CURL * pcurl, curl_infotype ntype, char * ptr, size_t size, std::string  * stream)
{
	int len  = (int)size;
	stream->append(ptr, len);
	return len;
}




CVVCurl::CVVCurl(void)
{
	m_pcurl = NULL ;
	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") ;
	m_cookiepath = _T("cookie.txt") ; //CUtil::GetRandTempPath(_T("_cookie.txt"));
}


CVVCurl::~CVVCurl(void)
{
	Release() ;
}


BOOL CVVCurl::Init(CString strProxyAddr,INT nPort) 
{
	m_nPort = nPort ;
	m_strProxyServer = strProxyAddr ;
	return InitCurlHandle() ;
}


void CVVCurl::Release() 
{
	ReleaseCurlHandle() ;
	DeleteCookieFile() ;
}


BOOL CVVCurl::InitCurlHandle() 
{
	if( NULL == m_pcurl)
	{
		m_pcurl = curl_easy_init() ;
	}
	if( NULL == m_pcurl ){
		ASSERT(FALSE) ;
		return FALSE ;
	}
	SetCurlHandleOpt() ;


	return TRUE ;
}


BOOL CVVCurl::SetCurlHandleOpt() 
{
	if( NULL == m_pcurl )
		return FALSE ;
	// 设置Agent
	curl_easy_setopt(m_pcurl, CURLOPT_USERAGENT, m_agent.c_str());
	// 官方下载的DLL并不支持GZIP,Accept-Encoding:deflate, gzip  
	curl_easy_setopt(m_pcurl, CURLOPT_ENCODING, "");
	//跳过服务器SSL验证,不使用CA证书  
	curl_easy_setopt(m_pcurl, CURLOPT_SSL_VERIFYPEER, 0L);
	//如果不跳过SSL验证,则可指定一个CA证书目录  
	//curl_easy_setopt(curl, CURLOPT_CAPATH, "this is ca ceat"); 
	//验证服务器端发送的证书,默认是 2(高),1(中),0(禁用)  
	curl_easy_setopt(m_pcurl, CURLOPT_SSL_VERIFYHOST, 0L);


	/* 与服务器通信交互cookie,默认在内存中,可以是不存在磁盘中的文件或留空 */  
	curl_easy_setopt(m_pcurl, CURLOPT_COOKIEFILE, m_cookiepath.c_str());   
	/* 与多个CURL或浏览器交互cookie,会在释放内存后写入磁盘文件 */  
	curl_easy_setopt(m_pcurl, CURLOPT_COOKIEJAR, m_cookiepath.c_str()) ;


	//设置重定向的最大次数  
	curl_easy_setopt(m_pcurl, CURLOPT_MAXREDIRS, 5); 
	// 设置自动设置refer字段
	curl_easy_setopt ( m_pcurl, CURLOPT_AUTOREFERER, 1 );
	//设置301、302跳转跟随location  
	curl_easy_setopt(m_pcurl, CURLOPT_FOLLOWLOCATION, 1);
	//抓取内容后,回调函数  
	curl_easy_setopt(m_pcurl, CURLOPT_WRITEFUNCTION, call_wirte_func);  
	curl_easy_setopt(m_pcurl, CURLOPT_WRITEDATA, &m_bodycontent );  
	//抓取头信息,回调函数  
	curl_easy_setopt(m_pcurl, CURLOPT_HEADERFUNCTION, header_callback );  
	curl_easy_setopt(m_pcurl, CURLOPT_HEADERDATA, &m_headcontent);  
	// 设置超时时间
	curl_easy_setopt(m_pcurl, CURLOPT_TIMEOUT, 10);
	// 禁用掉alarm这种超时
	curl_easy_setopt(m_pcurl, CURLOPT_NOSIGNAL, 1L);
	// 禁止重用TCP连接
	curl_easy_setopt(m_pcurl, CURLOPT_FORBID_REUSE, 1);


	// 打开调试
	curl_easy_setopt(m_pcurl, CURLOPT_VERBOSE, 1);
	curl_easy_setopt(m_pcurl, CURLOPT_DEBUGFUNCTION, debug_callback);
	curl_easy_setopt(m_pcurl, CURLOPT_DEBUGDATA, &m_debugcontent);
	
	// 判断是否是需要代理
	if( m_strProxyServer.size() > 0 && m_nPort > 0)
	{
		// 打开,允许重用TCP连接
		curl_easy_setopt(m_pcurl, CURLOPT_FORBID_REUSE, 0);
		// 第一种方法
		curl_easy_setopt(m_pcurl,CURLOPT_PROXY,m_strProxyServer.c_str());
		curl_easy_setopt(m_pcurl, CURLOPT_PROXYPORT, m_nPort);
		curl_easy_setopt(m_pcurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); 
		curl_easy_setopt(m_pcurl, CURLOPT_HTTPPROXYTUNNEL, 1L); 


	}


	return TRUE ;
}


BOOL CVVCurl::ReleaseCurlHandle() 
{
	if( NULL != m_pcurl)
	{
		curl_easy_cleanup(m_pcurl);
	}
	m_pcurl = NULL ;
	return TRUE ;
}


BOOL CVVCurl::DeleteCookieFile() 
{
	::DeleteFile(m_cookiepath.c_str());
	return TRUE ;
}


BOOL CVVCurl::OpenURL(std::string strURL,CURL_TYPE ntype)
{
	return OpenURL(strURL,_T(""),ntype) ;
}


BOOL CVVCurl::OpenURL(std::string strURL,std::string strPostData,CURL_TYPE ntype)
{
	m_headcontent = m_bodycontent = m_debugcontent = _T("");
	if( NULL == m_pcurl )
	{
		ASSERT(FALSE) ;
		return FALSE ;
	}


	if( ntype == CURL_POST || strPostData.size() > 0)
	{
		//curl_easy_setopt(m_pcurl,CURLOPT_HTTPGET,0);
		/* POST 数据 */  
		curl_easy_setopt(m_pcurl,CURLOPT_POST,1);
		if(strPostData.size() > 0)
		{
			curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDS, strPostData.c_str()); 
			curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDSIZE, strPostData.size());
		}
		else
		{
			curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDS, NULL); 
			curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDSIZE, 0);
		}
		//SetCurlHandleOpt() ;
	}else
	{
		// 禁用POST,直接GET请求
		//curl_easy_setopt(m_pcurl,CURLOPT_POST,0);
		//curl_easy_setopt(m_pcurl, CURLOPT_POSTFIELDS, NULL); 
		curl_easy_setopt(m_pcurl,CURLOPT_HTTPGET,1);
		//SetCurlHandleOpt() ;
	}


	try
	{
		// 远程URL,支持 http, https, ftp  
		curl_easy_setopt(m_pcurl, CURLOPT_URL, strURL.c_str()); 
		CURLcode nRet =  curl_easy_perform(m_pcurl);
		return CURLE_OK == nRet ;
	}
	catch (...)
	{
	}
	return FALSE ;
	
}


   2.4 编写代码

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


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

void CTestlibCurlDlg::OnBnClickedVisitButton()
{
	UpdateData(TRUE); 
	m_Url = m_Url.Trim();
	if( m_Url.GetLength() <= 0)
		return ;
	CVVCurl vvcurl ;
	vvcurl.Init() ;
	if( vvcurl.OpenURL(m_Url.GetBuffer()))
	{
		m_ContentEdit.Clear() ;
		m_ContentEdit.SetSel(0,-1,FALSE);
		m_ContentEdit.ReplaceSel(vvcurl.GetBodyContent(),FALSE) ;
	}
}

   2.5 调试

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

Linking...
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
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
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
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
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
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
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]

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

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


点此打发咖啡】[https://me.alipay.com/jekkay]

胡杨, Jekkay Hu

2014/2/18




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值