代码:
#include <curl/curl.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
using namespace std;
//下载缩略图表
string html_data;
size_t write_html(char * data, size_t size, size_t nmemb, void*)
{
html_data.append(data, size * nmemb);
return size * nmemb;
}
//为指定URL取待存储文件的名字
string make_image_file_name(string const& img_url, int index)
{
///抠扩展名
size_t dot_pos = img_url.rfind('.'); ///rfind:从尾部向前找
cout << "dot_pos = " << dot_pos << endl;
string ext;
if(dot_pos != string::npos)
{
string ext = img_url.substr(dot_pos);
cout << "ext = " << ext << endl;
}
size_t q_pos = ext.rfind('?');
if(q_pos != string::npos)///如果有?号
{
ext = ext.substr(0, q_pos);
cout << "???" << endl;
}
return std::to_string(index) + ext + ".jpg";
}
//将数据写入指定文件
size_t write_image(char* data, size_t size, size_t nmemb, void* userdata)
{ //userdate就是82行的第三个参数, 在libcurl库调用write_image时传递过来的,
//此处的userdata就是licurl库传递过来的,空指针形式ofs地址,
//所以此处先把userdata再转换成ofstream指针
ofstream* ofs = static_cast <ofstream*> (userdata);
ofs->write(data, size * nmemb);
return size * nmemb;
}
///下载大图
void download(string const& img_url, string const& filename)
{
if((img_url.substr(0, 6)) != "http:")
{
cerr << "无法处理:" << img_url << img_url << "。" << endl;
return; ///暂时只支持http: 协议
}
///ofs对象声明在此
ofstream ofs(filename, ios_base::out | ios_base::binary);
CURL * handle = curl_easy_init();
// 设置使用 ssl 访问 必应网站
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(handle, CURLOPT_URL, img_url.c_str());
/*CURLOPT_HEADER: http以及https协议中,
服务端返回的数据包含报头和报体两大部分。这里
我们明确指定我们不需要存储报头数据,
事实上该项默认就是0,因此可以和之前的
write_html()一样,不调用*/
curl_easy_setopt(handle, CURLOPT_HEADER, 0);
/*允许重定向,有些网站在访问其名为"http://www.abc.com/a"资源时,
出于某些目的会自动改为访问"http://www.abc.com/b"的资源,称为重定向。
此时将FOLLOW-LOCATION设置为真,表示我们接受这样的行为,
只要网站最终能返回图片数据即可。*/
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
//设定处理数据的函数为write_image()
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_image);
/*CURLOPT_WRITEDATA选项为将来调用处理数据的函数时,设定附加的参数,
通常称为"user data(用户数据)"。我们先将一个ofs对象的地址,强制转换为"void *"。
由此,将来调用curl_easy_perform()获得服务端的数据时,
libcurl库将调用write_image,并额外将"&ofs"以"void *"形式传递过去。
将&ofs传递给函数write_image。
这样的过程,是纯C语言编程过程中非常常见的小把戏。
将读到的数据,和ofs对象的地址(以空指针的形式)传递给write_image*/
curl_easy_setopt(handle, CURLOPT_WRITEDATA, static_cast<void*> (&ofs));
//有些网站反应慢,这里设置读取数据最多等20秒
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 20);
curl_easy_perform(handle);
curl_easy_cleanup(handle);
}
int main()
{
cout << "严正声明:本程序仅用于学习。\r\n"
<< "通过本程序下载的任何图片请在8小时之内删除,不得传播。\r\n"
<< "更不得使用于任何其他目的。\r\n" << endl;
cout << "请输入明星姓名的全拼(不要带空格或其他分隔): ";
string name;
cin >> name;
curl_global_init(CURL_GLOBAL_DEFAULT);
//通过curl_easy_init()得到一个CURL指针对象,
CURL* handle = curl_easy_init();
// 设置使用 ssl 访问 必应网站
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
string url = "http://cn.bing.com/images/search?q=" + name;
/*设置待执行的网络操作所必须的条件,第二个参数是枚举值,
用于标出所要设置的条件类型,设置目标URL就是CURLOPT_URL*/
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
/*设置当libcurl从服务端读到数据时,该调用哪个函数来处理这些数据,
我们设置的是自定义的write_html()函数。libcurl是纯C库,所以只能使用函数指针,
其他C++的“函数对象”,“function对象”和“Lambda表达式”统统不灵*/
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_html);
/*libcurl将连接指定URL,并从中读取结果,结果就是之前使用浏览器看到的网页。*/
curl_easy_perform(handle);
/* 清除指定CURL对象所占用的资源,与之前的handle = curl_easy_init()对应 */
curl_easy_cleanup(handle);
/* 用于收回libcurl全局初始化分配的资源,与curl_global_init()对应 */
curl_global_cleanup();
//保存成临时文件的逻辑,调试成功后可以删除
ofstream ofs("./data.html");
ofs << html_data;
string pre = "src=\"";
size_t len_of_pre = pre.length();
size_t begin_pos = html_data.find(pre, 0);
int index = 0;
while(begin_pos != string::npos)
{
begin_pos += len_of_pre;
size_t end_pos = html_data.find('\"', begin_pos);
// size_t end_pos = html_data.find('?', begin_pos);
if(end_pos == string::npos)
break;
string img_url = html_data.substr(begin_pos, end_pos - begin_pos);
string filename = ".\\images\\" + make_image_file_name(img_url, index++);
cout << img_url << " => " << filename << endl;
download(img_url, filename);
///下一张
begin_pos = html_data.find(pre, end_pos + 1);
} ///while
cout << "2=========" << endl;
curl_global_cleanup();
}
关键代码:
65行,使用ssl访问网络。因为现在通过http访问网络,后台,会转为用https来访问。需要额外设置CURL的CURLOPT_SSL_VERIFYPEER选项为0。这将告诉libcurl,可以使用加密的报文访问该资源,但不用验证(VERIFY)对方(PEER)身份。
74行,CURLOPT_HEADER: http以及https协议中,服务端返回的数据包含报头和报体两大部分。这里我们明确指定我们不需要存储报头数据,事实上该项默认就是0,因此可以和之前的
write_html()一样,不调用
80行,允许重定向,有些网站在访问其名为"http://www.abc.com/a"资源时,出于某些目的会自动改为访问"http://www.abc.com/b"的资源,称为重定向。此时将FOLLOW-LOCATION设置为真,表示我们接受这样的行为,只要网站最终能返回图片数据即可。
83行,设置数据的处理函数为write_image
92行,CURLOPT_WRITEDATA选项为将来调用处理数据的函数时,设定附加的参数,通常称为"user data(用户数据)"。我们先将一个ofs对象的地址,强制转换为"void *"。由此,将来调用curl_easy_perform()获得服务端的数据时,libcurl库将调用write_image,并额外将"&ofs"以"void *"形式传递过去。将&ofs传递给函数write_image。这样的过程,是纯C语言编程过程中非常常见的小把戏。将读到的数据,和ofs对象的地址(以空指针的形式)传递给write_image
ofs对象声明在61行。