如何在C程序中使用libcurl库下载网页内容

亿牛云.png

概述

爬虫是一种自动获取网页内容的程序,它可以用于数据采集、信息分析、网站监测等多种场景。在C语言中,有一个非常强大和灵活的库可以用于实现爬虫功能,那就是libcurl。libcurl是一个支持多种协议和平台的网络传输库,它提供了一系列的API函数,可以让开发者方便地发送和接收HTTP请求。

本文将介绍如何在C程序中使用libcurl库下载网页内容,并保存到本地文件中。同时,为了避免被目标网站封禁IP,我们还将使用代理IP技术,通过不同的IP地址访问网站。我们将参考爬虫代理的域名、端口、用户名、密码,来获取可用的代理IP。最后,我们将使用自定义写入回调函数,来处理网页内容的写入操作。

正文

1. 安装和配置libcurl库

要使用libcurl库,首先需要下载并安装它。我们可以从官网下载最新版本的源码包,也可以使用包管理器来安装预编译的二进制包。以Windows平台为例,我们可以使用Visual Studio命令行工具来编译libcurl库。具体步骤如下:

  • 下载并解压libcurl源码包
  • 打开Visual Studio命令行工具,并切换到源码包的winbuild目录下
  • 执行编译命令,例如:nmake /f Makefile.vc mode=dll VC=15 MACHINE=x64 DEBUG=no
  • 编译成功后,在builds目录下会生成相应的头文件、库文件和可执行文件

接下来,我们需要配置libcurl库的路径和链接选项。以Visual Studio为例,我们可以在项目属性中设置以下内容:

  • 在C/C++ -> 常规 -> 附加包含目录中添加libcurl头文件所在的路径
  • 在链接器 -> 常规 -> 附加库目录中添加libcurl库文件所在的路径
  • 在链接器 -> 输入 -> 附加依赖项中添加libcurl.lib

2. 初始化和设置libcurl句柄

要使用libcurl库发送HTTP请求,我们需要创建一个libcurl句柄,并对其进行一些必要的设置。具体步骤如下:

  • 调用curl_global_init函数初始化libcurl环境
  • 调用curl_easy_init函数创建一个libcurl句柄
  • 调用curl_easy_setopt函数设置一些选项,例如:
    • CURLOPT_URL:设置要访问的网址
    • CURLOPT_PROXY:设置代理服务器的地址和端口
    • CURLOPT_PROXYTYPE:设置代理服务器的类型,例如HTTP或SOCKS
    • CURLOPT_PROXYUSERPWD:设置代理服务器的用户名和密码
    • CURLOPT_WRITEFUNCTION:设置写入回调函数的指针
    • CURLOPT_WRITEDATA:设置写入回调函数的参数

3. 发送HTTP请求并处理响应

设置好libcurl句柄后,我们就可以发送HTTP请求了。具体步骤如下:

  • 调用curl_easy_perform函数执行HTTP请求
  • 调用curl_easy_getinfo函数获取一些有用的信息,例如:
    • CURLINFO_RESPONSE_CODE:获取HTTP响应的状态码
    • CURLINFO_PRIMARY_IP:获取目标网站的IP地址
    • CURLINFO_CONTENT_TYPE:获取响应的内容类型
  • 调用写入回调函数处理响应的内容,例如:
    • 打开一个本地文件
    • 将响应的内容写入文件
    • 关闭文件

4. 释放资源和清理环境

完成HTTP请求后,我们需要释放一些资源,并清理libcurl环境。具体步骤如下:

  • 调用curl_easy_cleanup函数释放libcurl句柄
  • 调用curl_global_cleanup函数清理libcurl环境

5.C代码示例

首先是回调函数的实现如下:

// 定义写入回调函数的实现
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {// 计算接收到的数据的字节数size_t bytes = size * nmemb;// 将数据写入文件中fwrite(ptr, size, nmemb, (FILE *)stream);// 返回写入的字节数return bytes;
}

这个函数的参数和返回值都是由libcurl库定义的,我们只需要按照规范来实现即可。函数的作用是将接收到的数据(ptr)写入到指定的文件流(stream)中,并返回写入的字节数(bytes)。这样,libcurl库就可以知道是否有数据丢失或错误发生。
下面是完整的C代码,你可以编译运行,看看效果如何。

#include <stdio.h>
#include "curl/curl.h"size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream);// 亿牛云 爬虫代理 设置代理服务器域名、端口、用户名、密码
#define PROXY_HOST "http://www.16yun.cn"
#define PROXY_PORT "8080"
#define PROXY_USER "16YUN"
#define PROXY_PASS "16IP"
#define URL "http://www.example.com"
#define FILENAME "example.html"int main(void) {// 初始化 curl 库CURLcode code = curl_global_init(CURL_GLOBAL_ALL);if (code != CURLE_OK) {fprintf(stderr, "初始化失败: %s\n", curl_easy_strerror(code));return 1;}// 获取 curl 库的版本信息curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);printf("curl 库的版本: %s\n", data->version);// 创建一个 easy 句柄CURL *curl = curl_easy_init();if (curl == NULL) {fprintf(stderr, "创建句柄失败\n");curl_global_cleanup();return 2;}// 设置 URLcode = curl_easy_setopt(curl, CURLOPT_URL, URL);if (code != CURLE_OK) {fprintf(stderr, "设置 URL 失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 3;}// 设置代理服务器code = curl_easy_setopt(curl, CURLOPT_PROXY, PROXY_HOST ":" PROXY_PORT);if (code != CURLE_OK) {fprintf(stderr, "设置代理服务器失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 4;}// 设置代理类型code = curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);if (code != CURLE_OK) {fprintf(stderr, "设置代理类型失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 5;}// 设置代理认证code = curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, PROXY_USER ":" PROXY_PASS);if (code != CURLE_OK) {fprintf(stderr, "设置代理认证失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 6;}// 设置用户代理char *user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36";code = curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);if (code != CURLE_OK) {fprintf(stderr, "设置用户代理失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 7;}// 设置 Cookiechar *cookies = "your_cookie_data_here";code = curl_easy_setopt(curl, CURLOPT_COOKIE, cookies);if (code != CURLE_OK) {fprintf(stderr, "设置 Cookie 失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 8;}// 设置写入数据的回调函数code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);if (code != CURLE_OK) {fprintf(stderr, "设置回调函数失败: %s\n", curl_easy_strerror(code));curl_easy_cleanup(curl);curl_global_cleanup();return 9;}// 打开本地文件FILE *file = fopen(FILENAME, "wb");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", FILENAME);curl_easy_cleanup(curl);curl_global_cleanup();return 10;}// 设置写入数据的文件指针code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);if (code != CURLE_OK) {fprintf(stderr, "设置文件指针失败: %s\n", curl_easy_strerror(code));fclose(file);curl_easy_cleanup(curl);curl_global_cleanup();return 11;}// 执行传输code = curl_easy_perform(curl);if (code != CURLE_OK) {fprintf(stderr, "执行传输失败: %s\n", curl_easy_strerror(code));fclose(file);curl_easy_cleanup(curl);curl_global_cleanup();return 12;}// 获取响应状态码long status;code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);if (code == CURLE_OK) {printf("响应状态码: %ld\n", status);} else {fprintf(stderr, "获取状态码失败: %s\n", curl_easy_strerror(code));}// 获取目标 IP 地址char *ip;code = curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, &ip);if (code == CURLE_OK) {printf("目标 IP 地址: %s\n", ip);free(ip); // 需要释放字符串指针} else {fprintf(stderr, "获取 IP 地址失败: %s\n", curl_easy_strerror(code));}// 获取内容类型char *type;code = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &type);if (code == CURLE_OK) {printf("内容类型: %s\n", type);free(type); // 需要释放字符串指针} else {fprintf(stderr, "获取内容类型失败: %s\n", curl_easy_strerror(code));}// 关闭文件fclose(file);// 清理 easy 句柄curl_easy_cleanup(curl);// 清理全局资源curl_global_cleanup();return 0;
}// 写入数据的回调函数
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);if (written != size * nmemb) {fprintf(stderr, "写入文件出错\n");return -1; // 返回一个负值,停止传输}return written;
}

结语

本文介绍了如何在C程序中使用libcurl库下载网页内容,并保存到本地文件中。我们还使用了代理IP技术,来绕过目标网站的反爬措施。我们使用了自定义写入回调函数,来处理网页内容的写入操作。通过这个示例,我们可以学习到libcurl库的基本用法和一些高级特性,以及如何在C语言中实现爬虫功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/108322.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

天锐绿盾透明加密、半透明加密、智能加密这三种不同加密模式的区别和适用场景——@德人合科技-公司内部核心文件数据、资料防止外泄系统

由于企事业单位海量的内部数据存储情况复杂&#xff0c;且不同公司、不同部门对于文件加密的需求各不相同&#xff0c;单一的加密系统无法满足多样化的加密需求。天锐绿盾企业加密系统提供多种不同的加密模式&#xff0c;包括透明加密、半透明加密和智能加密&#xff0c;用户可…

解码自然语言处理之 Transformers

自 2017 年推出以来&#xff0c;Transformer 已成为机器学习领域的一支重要力量&#xff0c;彻底改变了翻译和自动完成服务的功能。 最近&#xff0c;随着 OpenAI 的 ChatGPT、GPT-4 和 Meta 的 LLama 等大型语言模型的出现&#xff0c;Transformer 的受欢迎程度进一步飙升。这…

Burstormer论文阅读笔记

这是CVPR2023的一篇连拍图像修复和增强的论文&#xff0c;一作是阿联酋的默罕默德 本 扎耶得人工智能大学&#xff0c;二作是旷视科技。这些作者和CVPR2022的一篇BIPNet&#xff0c;同样是做连拍图像修复和增强的&#xff0c;是同一批。也就是说同一个方向&#xff0c;22年中了…

设计模式:简单工厂模式(C#、JAVA、JavaScript、C++、Python、Go、PHP):

简介&#xff1a; 简单工厂模式&#xff0c;它提供了一个用于创建对象的接口&#xff0c;但具体创建的对象类型可以在运行时决定。这种模式通常用于创建具有共同接口的对象&#xff0c;并且可以根据客户端代码中的参数或配置来选择要创建的具体对象类型。 在简单工厂模式中&am…

MySQL --- 聚合查询

聚合查询相当是行和行之间进行运算。 下文中的所有示例操作都是基于此表&#xff1a; 聚合函数 count(列名) select count(列名) from 表名; 统计该表中该列的行数&#xff0c;但是 null 值不会统计在内&#xff0c;但是如果写为 count(*) null 也会算进去&#xff08;就算…

轻松完成Figma导入Sketch的在线方法

众所周知&#xff0c;Figma支持Sketch文件的导入&#xff0c;但Figma不支持Sketch文件的导出&#xff0c;而Sketch不能直接打开Figma文件&#xff0c;在这种情况下&#xff0c;如何将Figma导入Sketch&#xff1f;别担心&#xff0c;其实借助免费的在线工具即时设计&#xff0c;…

在 Android 上恢复已删除音乐的 5 种简单方法

人们经常将重要的音乐文件保存在智能手机上&#xff0c;以方便随时随地收听自己喜欢的曲目。但是&#xff0c;如果这些珍贵的音乐文件因软件故障或硬件故障而被意外删除或丢失怎么办&#xff1f;这将是许多音乐爱好者的噩梦&#xff01; 如果您也是这些人中的一员&#xff0c;…

stm32备份

存储器的分类&#xff1a; 存储器首先根据断电后存储的数据是否会丢失&#xff0c;可以分为易失存储器和非易失存储器&#xff0c;易失存储器主要应用于内存&#xff0c;非易失存储器主要用于外存。 易失存储器以RAM随机存储器为代表&#xff0c;随机的含义是存储器中的数据读取…

SRS Config 一 基础配置

Config srs 流媒体服务配置官方文档已经很详细了&#xff0c;本文仅记录部分配置过程 srs.conf同级目录下 新建 self.conf 仿照srs.conf 添加基础配置 1 rtmp RTMP是直播的事实标准&#xff0c;这么多年以来一直是使用最广泛的直播协议。 然而Adobe公司没有一直更新RTMP协…

icg模块clock gating解析

// 两种形式&#xff1a; 与门形式 或门形式 三个用途&#xff1a; 用于关断时钟&#xff0c;降低功耗用于动态时钟切换是防止毛刺的产生用于时钟分频 解析&#xff1a;与门形式 解析&#xff1a;或门形式

win11 搭建Apache webdav 设置用户名密码 加密授权访问以及多个不同目录访问

Apache webdav 的搭建应该比较简单,但是搭建后还遇到了一些问题,也就是设置了访问用户名密码,咋就不生效呢,苦苦思索两日,终于发现了问题,本文就是分两个方面来编写 一、搭建 1.下载Apache 官网下载: https://www.apachehaus.com/cgi-bin/download.plx 2.下载后解压…

Kubernetes 集群部署 Prometheus 和 Grafana

Kubernetes 集群部署 Prometheus 和 Grafana 文章目录 Kubernetes 集群部署 Prometheus 和 Grafana一.部署 node-exporter1.node-exporter 安装2.部署 node-exporter 二.部署Prometheus1.Prometheus 安装和配置&#xff08;1&#xff09;创建 sa 账号&#xff0c;对 sa 做 rbac…

Flow深入浅出系列之更聪明的分享 Kotlin Flows

Flow深入浅出系列之在ViewModels中使用Kotlin FlowsFlow深入浅出系列之更聪明的分享 Kotlin FlowsFlow深入浅出系列之使用Kotlin Flow自动刷新Android数据的策略 Flow深入浅出系列之更聪明的分享 Kotlin Flows 使生命周期对上游流有效&#xff0c;以跳过不必要的工作。这是一…

mmlab 做实验

首先 下载项目完整代码&#xff0c;在pycharm中打开 1. comfig 中有各种网络模型&#xff0c;可以直接使用训练好的预训练模型&#xff0c;尽量不要改动网络模型的结构 2. 18表示网络机构18层&#xff0c;8是每个卡的batch&#xff0c;cifar10 是数据集 3.配置文件解析 4. …

解决github打开慢的问题

1&#xff0c;修改hosts&#xff08;可以从这个链接 https://raw.hellogithub.com/hosts 获取对应的host配置&#xff09;。 140.82.112.3 github.com 151.101.1.194 github.global.ssl.fastly.net 2&#xff0c;刷新dns缓存。 # 打开CMD运行如下命令 ipconfig /flushdns 之…

【unity】【VR】白马VR课堂系列-VR开发核心基础04-主体设置-XR Rig的引入和设置

接下来我们开始引入并构建XR Rig。 你可以将XR Rig理解为玩家在VR世界中的替身。 我们先删除Main Camera&#xff0c;在Hierarchy右键点击删除。 然后再在场景层右键选择XR下的XR Origin。这时一个XR Origin对象就被添加到了Hierarchy。 重设XR Origin的Position和Rotation…

windows内网渗透正向代理

内网渗透正向代理 文章目录 内网渗透正向代理1 正向代理图2 环境准备2.1 正向代理需求&#xff1a; 3 网卡配置3.1 【redream】主机3.2 【base】主机双网卡3.3 【yvkong】网卡设置 4 启动4.1【redream】网卡配置&#xff1a;4.2【base】网卡配置&#xff1a;4.3【yvkong】网卡地…

配置VScode开发环境-CUDA编程

如果觉得本篇文章对您的学习起到帮助作用&#xff0c;请 点赞 关注 评论 &#xff0c;留下您的足迹&#x1f4aa;&#x1f4aa;&#x1f4aa; 本文主要介绍VScode下的CUDA编程配置&#xff0c;因此记录以备日后查看&#xff0c;同时&#xff0c;如果能够帮助到更多人&#xf…

KVM/qemu安装UOS 直接让输入用户密码

错误信息 安装后出现&#xff1a; 1、点击刚刚建立的虚拟机最上角感叹号&#xff08;设备管理器&#xff09; ----新建硬件---输入----类型&#xff1a;【通用 USB Mouse】。 ----新建硬件---输入----类型&#xff1a;【通用 USB keyboard】。 2、在设备管理器中----新建硬…

Compose Canvas基础(2) 图形转换

Compose Canvas基础&#xff08;2&#xff09;图形转换 前言平移 translate缩放 scale旋转 rotate自定义绘图区域及绘制内边距inset组合转换 withTransform完整代码总结 上一篇文章 Compose Canvas基础&#xff08;1&#xff09; drawxxx方法 前言 阅读本文需要一定compose基…