如何在Windows+VS2005使用最新靜態libcurl 7.35.0獲取網頁數據,支持HTTPS

每次在電影中看到他出場,都感覺正能量來了~~~


地址: http://blog.csdn.net/hujkay
作者:Jekkay Hu([email protected])
關鍵詞: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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章