《白话C++》第13章 网络 Page647 获取指定URL图片 http

代码:

#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行

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值