libcurl的使用

31 篇文章 1 订阅

简介

libcurl是一个客户端URL传输库,支持很多协议(自行查看官方简介:https://curl.haxx.se/libcurl/),简而言之,这个库能爬网页,有了这个库,就不需要自己写socket来爬网页了,方便了很多。
此处使用的语言为C++,虽然libcurl有C++的binding,但是那样就得看两份文档了,太麻烦,况且官方文档提到了在C++下使用时,唯一需要注意的是回调函数不能为非静态类成员函数。(官方文档地址https://curl.haxx.se/libcurl/c/libcurl-tutorial.html

类定义

下面是我用libcurl写的一个类html_getter的头文件:

//html_getter.h
#ifndef HTML_GETTER_H_INCLUDED
#define HTML_GETTER_H_INCLUDED

#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <atomic>

#include "log.h"

class html_getter
{
public:
    html_getter();
    std::string *get_string(const std::string &url);
    ~html_getter();

private:
    static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
    static std::atomic_int init;   //identify whether the curl_global_init() function has been called
};

#endif // HTML_GETTER_H_INCLUDED

除了构造函数和析构函数之外,还有两个成员函数:write_callback()和get_string()、一个成员变量:init。write_callback()和init用于支持libcurl,get_string()是我们的功能接口,调用时,传入一个url,即可将该url对应的网页内容以string *返回。

类实现

类的实现代码如下:

//html_getter.cpp
#include "html_getter.h"

std::atomic_int html_getter::init(0);

html_getter::html_getter()
{
    if (init == 0) {
        curl_global_init(CURL_GLOBAL_ALL);
    }
    init++;
}

size_t html_getter::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    std::string *str = (std::string *)userdata;
    size_t realsize = size * nmemb;
    char *temp = (char *)malloc(realsize * sizeof(char) + 1);
    memcpy(temp, ptr, realsize);
    temp[realsize] = '\0';
    *str += temp;
    free(temp);
    return realsize;
}

std::string *html_getter::get_string(const std::string &url)
{
    CURL *curl = curl_easy_init();
    CURLcode res;
    std::string buffer = "";
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_COOKIE, "ViewMode=contents");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        buffer = "";
    }
    curl_easy_cleanup(curl);
    return new std::string(buffer);
}

html_getter::~html_getter()
{
    init--;
    if (init == 0) {
        curl_global_cleanup();
    }
}

先来说说init这个静态成员,它用来标识当前有几个html_getter实例,为什么要记录这个信息呢?是因为libcurl在调用其功能函数之前需要先调用初始化函数curl_global_init(),功能函数使用结束之后需要调用其清理函数curl_global_cleanup()。在初始化一个html_getter实例时,如果发现init值为0,则需要调用初始化函数;与此相反,当释放一个html_getter实例时,如果发现init值为0,则需要调用清理函数。
初始化函数curl_global_init()可传入的参数有很多,可以用来初始化不同的子模块。官方文档提到了“CURL_GLOBAL_ALL will make it initialize all known internal sub modules, and might be a good default option. ”,因此我也采用了CURL_GLOBAL_ALL作为初始化参数。
libcurl有两类API——easy interface和multi interface,前者是同步的,后者是前者的异步实现。前者较为容易且“Numerous applications have been built using this.”,因此我在这里使用前者。

Easy interface

easy interface的基本使用流程如下:

Created with Raphaël 2.1.0 开始 初始化并获得句柄 curl_easy_init() 设置参数 curl_easy_setopt() 是否设置完毕 执行 curl_easy_perform() 清理 curl_easy_cleanup() 结束 yes no

初始化并获得句柄

函数原型为CURL *curl_easy_init( ),在这一步得到的句柄需要在后面几步中用到,因此需要将这个句柄保存下来,留待使用。

设置参数

设置参数使用的函数原型为CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter),第一个参数为句柄,在第一步得到,第二个参数为参数类型,它有能够设置的参数类型有很多,因为类的功能为使用GET请求网页,因此此处用到了四个参数:CURLOPT_URL设置请求URL、CURLOPT_COOKIE设置COOKIE、CURLOPT_WRITEFUNCTION设置写回调函数、CURLOPT_WRITEDATA设置写缓冲区(最后会被传给回调函数)。其中写回调函数的原型为size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)它在执行了curl_easy_perform()函数后,获取到资源后被调用,ptr为获取的资源,获取的资源的大小为size * nmemb,并且资源不会以’\0’结尾。最后的userdata即为使用CURLOPT_WRITEDATA设置参数时传入的参数,一般我们会把得到的资源经过处理后保存在这里。

写回调函数的坑

每次写回调函数被调用时,ptr指向的资源是有大小限制的,一般不超过16K。因此如果资源比较大,调用了curl_easy_perform()后写回调函数会被调用多次,资源会被分批传入,所以我这里使用了std::string类型对资源进行拼接。另外一个坑是传输资源时,即使资源足够小,也可能会被切分,也就是说,即使资源只有8K,也可能会被切分成4*2K进行处理,因此写回调函数一定要被设计成能处理分批数据的。

执行

函数原型为CURLcode curl_easy_perform(CURL * easy_handle ),执行后如果没有出错(通过返回值判断是否出错),就会调用写回调函数进行处理。

清理

函数原型为void curl_easy_cleanup(CURL * handle),没什么可说的,有init就要有cleanup,有open就要有close。

以上就是libcurl简单的应用,更多内容可参见官方文档和样例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值