C语言爬取HTML-爬取壁纸 文末附源码

news2024/11/20 0:33:40

前言:这学期计算机软件课程设计的其中一个题目是使用C语言爬取HTML,本打算使用C语言的CSpidr库来实现,但是因为它的依赖liburi没有找到在哪里安装,所以放弃了这个想法,使用的是curl以及libxml2这两个库,能够提供访问网页以及xpath解析的功能就够用了。

  • 项目使用C语言爬取壁纸,爬取的网站是https://wallhaven.cc

  • 开发环境使用的是Ubuntu22.04,编译器gcc 11.3,使用makefile管理项目

依赖库的安装:

sudo apt update
sudo apt install curl libxml2-dev

项目结构:

项目由两个文件组成,一个是main.c源代码,另一个是用于makefile编译的makefile文件。

├── main.c
└── Makefile

程序编译并执行完成之后项目目录如下:

image-20230518155936792

  • img:存放爬取的图片
  • main main.o可执行文件
  • result.txt:图片页面的URL
  • url.txt:每张图片的url

流程图如下:

img

使用说明:

该程序使用make命令对程序进行编译

img

该程序可以通过命令行参数指定起始页和结束页,命令如下:

sudo ./main start_page end_page

img

爬取的壁纸
在这里插入图片描述
以后再也不缺壁纸啦

源码:

makefile

# CC = gcc
CFLAGS = -I/usr/include/libxml2
LDFLAGS = -lcurl -lxml2

TARGET = main
SRCS = main.c
OBJS = $(SRCS:.c=.o)

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) $(OBJS) -o $(TARGET) $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)

源码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

void get_imgpage_url(int start_page, int end_page); // 获取图片页面的url
void get_img_url();                                 // 获取图片的url
void download_img(char *url, char *outfilename);    // 下载图片

#define USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 Edg/104.0.1293.63"

typedef struct
{
    char *buffer;
    size_t size;
} MemoryStruct;

// 回调函数,将获取的数据写入缓冲区
size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t real_size = size * nmemb;
    MemoryStruct *mem = (MemoryStruct *)userp;

    mem->buffer = realloc(mem->buffer, mem->size + real_size + 1);
    if (mem->buffer == NULL)
    {
        printf("Failed to allocate memory.\n");
        return 0;
    }

    memcpy(&(mem->buffer[mem->size]), contents, real_size);
    mem->size += real_size;
    mem->buffer[mem->size] = '\0';

    return real_size;
}

/// @brief 获取每一页的图片链接
/// @param start_page
/// @param end_page
void get_imgpage_url(int start_page, int end_page)
{
    CURL *curl;
    CURLcode res;
    MemoryStruct response;
    response.buffer = malloc(1);
    response.size = 0;

    printf("初始化libcurl...\n");
    curl_global_init(CURL_GLOBAL_DEFAULT); // 初始化libcurl
    printf("创建会话...\n");
    curl = curl_easy_init(); // 创建会话
    if (curl)
    {
        FILE *file = fopen("result.txt", "w"); // 打开文件以保存结果
        if (file == NULL)
        {
            printf("Failed to open file.\n");
            return;
        }

        // 循环获取每一页的图片链接
        for (int page = start_page; page <= end_page; ++page)
        {
            char url[100];                                                            // 保存URL
            snprintf(url, sizeof(url), "https://wallhaven.cc/toplist?page=%d", page); // 设置URL

            curl_easy_setopt(curl, CURLOPT_URL, url);                      // 设置URL
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 设置回调函数
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);  // 设置回调函数的参数

            printf("发起请求...\n");
            res = curl_easy_perform(curl); // 执行请求
            if (res != CURLE_OK)
            {
                printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
                continue;
            }

            htmlDocPtr doc = htmlReadMemory(response.buffer, response.size, NULL, NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); // 解析HTML
            if (doc == NULL)
            {
                printf("Failed to parse HTML.\n");
                continue;
            }

            xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); // 创建XPath上下文
            if (xpathCtx == NULL)
            {
                printf("Failed to create XPath context.\n");
                xmlFreeDoc(doc);
                continue;
            }

            for (int i = 0; i < 20; i++) // 获取每页的20个图片链接
            {
                char xpath[] = "/html/body/main/div[1]/section/ul/li[%d]/figure/a/@href";
                snprintf(xpath, sizeof(xpath), "/html/body/main/div[1]/section/ul/li[%d]/figure/a/@href", i + 1);
                xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((xmlChar *)xpath, xpathCtx); // 评估XPath表达式
                if (xpathObj == NULL)
                {
                    printf("Failed to evaluate XPath expression.\n");
                    xmlXPathFreeContext(xpathCtx);
                    xmlFreeDoc(doc);
                    continue;
                }

                xmlNodeSetPtr nodes = xpathObj->nodesetval; // 获取结果
                for (int i = 0; i < nodes->nodeNr; ++i)
                {
                    xmlChar *href = xmlNodeListGetString(doc, nodes->nodeTab[i]->xmlChildrenNode, 1);
                    if (href != NULL)
                    {
                        fprintf(file, "%s\n", href); // 将结果写入文件
                        xmlFree(href);
                    }
                }
                xmlXPathFreeObject(xpathObj);
            }

            xmlXPathFreeContext(xpathCtx);
            xmlFreeDoc(doc);
            printf("Page %d 链接获取完成.\n", page);
        }

        fclose(file);
        free(response.buffer);
        curl_easy_cleanup(curl);
    }
    else
    {
        printf("curl_easy_init() failed.\n");
    }

    curl_global_cleanup();
    printf("开始解析图片链接...\n");
    get_img_url();
}
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
    size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
    return written;
}
void download_img(char *url, char *outfilename)
{
    printf("正在下载%s\n",outfilename);
    CURL *curl;
    FILE *fp;
    CURLcode res;
    // char url[256] = "https://w.wallhaven.cc/full/p9/wallhaven-p9w7l9.png";
    // char outfilename[256] = "image.png";

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);

        // 打开当前目录下的img文件夹,没有则创建
        DIR *dir = opendir("img");
        if (dir == NULL)
        {
            mkdir("img", 0777);
        }
        closedir(dir);
        // 打开img文件夹下的图片,没有则创建
        char img_path[256] = "img/";
        strcat(img_path, outfilename);
        fp = fopen(img_path, "wb");
        if (fp)
        {
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
            res = curl_easy_perform(curl);
            if (res != CURLE_OK)
            {
                fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            }
            else
            {
                printf("Image saved successfully.\n");
            }
            fclose(fp);
        }
        else
        {
            fprintf(stderr, "Failed to create file: %s\n", outfilename);
        }

        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
}
void get_img_url()
{
    FILE *file;
    FILE *furl;
    char url[256];
    char outfilename[256];
    int count = 1;

    file = fopen("result.txt", "r");
    furl = fopen("url.txt", "w");
    if (file)
    {
        while (fgets(url, sizeof(url), file) != NULL)
        {
            // 去除换行符
            url[strcspn(url, "\n")] = '\0';

            //

            // 访问页面并获取内容
            CURL *curl = curl_easy_init();
            if (curl)
            {
                curl_easy_setopt(curl, CURLOPT_URL, url);
                curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

                MemoryStruct html_content;
                html_content.buffer = malloc(1);
                html_content.size = 0;

                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
                curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&html_content);
                // printf("start curl_easy_perform\n");
                CURLcode res = curl_easy_perform(curl);
                if (res != CURLE_OK)
                {
                    fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
                    curl_easy_cleanup(curl);
                    free(html_content.buffer);
                    return;
                }

                // 解析HTML内容
                // printf("start Parsing\n");
                xmlDocPtr doc = htmlReadMemory(html_content.buffer, html_content.size, "noname.html", NULL, HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
                xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);

                // 执行XPath查询,获取图片URL
                char *xpathExpr = "//*[@id='wallpaper']/@src";
                xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((xmlChar *)xpathExpr, xpathCtx);
                xmlNodeSetPtr nodes = xpathObj->nodesetval;

                if (nodes != NULL && nodes->nodeNr > 0)
                {
                    xmlChar *imgUrl = xmlNodeListGetString(doc, nodes->nodeTab[0]->xmlChildrenNode, 1);
                    if (imgUrl != NULL)
                    {
                        // fwrite(imgUrl, strlen(imgUrl), 1, furl);// 将结果写入文件并加换行符
                        fprintf(furl, "%s\n", imgUrl);
                        printf("图片URL: %s\n", imgUrl);
                        // print_image_url((const char *)imgUrl);
                        xmlFree(imgUrl);
                    }
                    else
                    {
                        printf("imgUrl is NULL\n");
                    }
                }
                xmlXPathFreeObject(xpathObj);
                xmlXPathFreeContext(xpathCtx);
                xmlFreeDoc(doc);

                // free(html_content.buffer);
            }
            curl_easy_cleanup(curl);
        }
        printf("图片链接获取完成.\n准备下载\n");
    }
    fclose(file);
    fclose(furl);

    furl = fopen("url.txt", "r");
    // 读取url.txt文件,download图片
    if (furl)
    {
        count = 1;
        while (fgets(url, sizeof(url), furl) != NULL)
        {
            // 去除换行符
            url[strcspn(url, "\n")] = '\0';
            // 根据读取url的后3位判断图片的类型,并设置图片的后缀名,后缀名还需要有count序号
            char *suffix = url + strlen(url) - 3;
            if (strcmp(suffix, "jpg") == 0)
            {
                strcpy(outfilename, "image");
                char count_str[10];
                sprintf(count_str, "%d", count);
                strcat(outfilename, count_str);
                strcat(outfilename, ".jpg");
            }
            else if (strcmp(suffix, "png") == 0)
            {
                strcpy(outfilename, "image");
                char count_str[10];
                sprintf(count_str, "%d", count);
                strcat(outfilename, count_str);
                strcat(outfilename, ".png");
            }
            else if (strcmp(suffix, "gif") == 0)
            {
                strcpy(outfilename, "image");
                char count_str[10];
                sprintf(count_str, "%d", count);
                strcat(outfilename, count_str);
                strcat(outfilename, ".gif");
            }
            else
            {
                printf("图片类型错误\n");
                continue;
            }
            download_img(url, outfilename);
            count++;
        }
    }
    else
    {
        printf("Failed to open furl.\n");
    }
    fclose(furl);
}

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("Usage: %s <start_page> <end_page>\n", argv[0]);
        return 0;
    }
    else if (atoi(argv[1]) > atoi(argv[2]))
    {
        printf("<start_page> must 大于 <end_page>\n");
    }
    else
    {
        printf("start page: %s\n", argv[1]);
        printf("end page: %s\n", argv[2]);
        get_imgpage_url(atoi(argv[1]), atoi(argv[2]));
    }
    return 0;
}

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

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

相关文章

这几款实用且有趣的软件不容错过

软件一&#xff1a;天若ocr 这款Windows平台的天若OCR文字识别工具一定更适合你。 软件作者来自天若游心&#xff0c;我爱破解。 发布一年半以来一直深受好评&#xff0c;更新速度也非常快。 最近&#xff0c;它增加了批次识别功能。 软件二&#xff1a;腾讯柠檬精简版 除了Q…

P2233 [HNOI2002]公交车路线

题目描述 在长沙城新建的环城公路上一共有 8 个公交站&#xff0c;分别为 A、B、C、D、E、F、G、H。公共汽车只能够在相邻的两个公交站之间运行&#xff0c;因此你从某一个公交站到另外一个公交站往往要换几次车&#xff0c;例如从公交站 A 到公交站 D&#xff0c;你就至少需要…

keycloak介绍与使用示例,超时时间设置

keycloak介绍 Keycloak是一款由Red Hat开源社区开发的开放源代码的身份和访问管理解决方案&#xff0c;它提供了安全的单点登录(SSO)、多因素身份验证、社交登录和基于角色的访问控制等功能Keycloak基于OAuth 2.0和OpenID Connect协议&#xff0c;并支持SAML 2.0&#xff0c;可…

spring Bean的循环依赖问题

public class Husband {private String name;private Wife wife;public void setName(String name) {this.name name;}public String getName() {return name;}public void setWife(Wife wife) {this.wife wife;}// toString()方法重写时需要注意&#xff1a;不能直接输出wif…

【计算机网络基础】章节测试2 物理层

文章目录 判断题选择题辨析题应用题 判断题 现在的无线局域网常用的频段是2.8GHz和5.4GHz。 多模光纤只适合于近距离传输。√ 数据在计算机内部多采用串行传输方式&#xff0c;但在通信线路上多采用并行传输方式。 统计时分复用可以按需动态分配时隙。√ 相对于同步时分复用…

卷麻了,新来的00后实在是太卷了...

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

geoserver切片数据本地缓存和层级配置

很多业务场景中&#xff0c;我们会用到图层切片功能&#xff0c;默认情况下&#xff0c;每次调用都是新的重新切片&#xff0c;这样在性能上存在一定问题&#xff1b;基于此我们可以进行本地缓存切片&#xff0c;及此地理位置只进行一次切片处理&#xff0c;数据缓存在本地磁盘…

基于Python实现线性分类器

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 在机器学习领域&#xff0c;分类的目标是指将具有相似特征的对象聚集。而一个线性分类器则透过特征的线性组合来做出分类决定&#xff0c;以达到此种目的。对象的特征通常被描述为特征值&#xff0c;而在向量中则描述为特征向…

Visual studio 配置intel realsense sdk环境

前面的部分已经有博主写过了&#xff0c;这里就不赘述了&#xff0c;附上链接&#xff1a;VS配置使用realsense相机SDK 仅仅配置上述文章中的部分&#xff0c;运行realsense example的部分例子程序时会出现找不到glfw3.h的问题。我查找了github上的提问发现这个问题原因是没有…

网络安全中NISP二级证书和CISP证书的优势有什么

优势&#xff1f;那就需要多个点展开说说啦~ 1.权威含金量高 我国信息安全领域唯一的国家级注册安全专业人员证书 2.就业面宽&#xff0c;企业优先选择证书持有者 通过专业培训和考试提高个人信息安全从业水平&#xff0c;证明具备从事信息安全技术和管理工作的能力&#x…

opencv二值化详解

大家好&#xff0c;今天来跟大家讲讲 opencv二值化。 先从一个比较经典的方法开始讲解&#xff0c;看 opencv官方文档&#xff1a; 二值化&#xff08;binary&#xff09;的定义&#xff1a;在一个输入图像中&#xff0c;将其一个像素点设置为0,将其两个像素点设置为1。 二值化…

开发笔记之:文件读取值溢出bug分析(QT C++版)

&#xff08;1&#xff09;引言 以下是QT C读取数据文件&#xff08;QDataStream&#xff09;的代码&#xff1a; /*** 按双字读取* param fis 文件输入流* param isBigEndian 是否大头&#xff08;字节序&#xff09;* return 双字值*/ DWORD FsFileUtil::readAsD…

怎么用问卷工具做市场调研?

对于希望开发新产品或服务、拓展新市场或确定潜在客户的公司来说&#xff0c;市场调查是一个至关重要的过程。然而&#xff0c;进行市场调查可能既耗时又昂贵&#xff0c;特别是在涉及对大量人群进行调查的情况下。今天&#xff0c;小编将来聊一聊调查问卷工具如何帮助企业进行…

微信小程序-基础知识

文章目录 AppIdOpenIDUnionId处理方法session_key AppId appid 是微信账号的唯一标识&#xff0c;这个是固定不变的&#xff1b; 如果了解微信公众号开发的就需要注意一下&#xff0c;小程序的appid 和 公众号的appid 是不一致的 OpenID 为了识别用户&#xff0c;每个用户针…

如何成为自动化测试工程师?8年测试总结,自动化测试岗晋升的技能...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

程序设计入门——C语言2023年5月18日

程序设计入门——C语言 第二周 计算表达式 课程来源&#xff1a;链接: 浙江大学 翁恺 程序设计入门——C语言 学习日期&#xff1a;2023年5月18日 第二周 计算 表达式 有两个变量a和b&#xff0c;交换a和b的值。 老师举例&#xff1a;有两杯液体&#xff0c;一杯茶&#xff…

03)FastDFS配置nginx 服务,使用http方式访问图片

FastDFS是没有文件访问功能的,需要借助其他工具实现图片HTTP访问的。 没安装nginx时比如前端html网页想获取 FastDFS的一张图片显示,需要java写个controller,然后使用 FastDFS-java client客户端调用文件获取api,HttpServletResponre在返回图片流.给前端显示。 安装了nginx…

HTTP介绍、原理

HTTP 与 HTTPS 有哪些区别&#xff1f; HTTP 是超文本传输协议&#xff0c;信息是明文传输&#xff0c;存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷&#xff0c;在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议&#xff0c;使得报文能够加密传输。HTTP 连接建立相…

带头 双向 循环 链表——C语言实现

既然有带头 那么就有不带头 为什么我要将带头 而不讲不带头&#xff1f; 在之前我讲单链表时就说过 如果不带头会出现讨论的情况 为什么会出现讨论的情况 假设链表有一个节点 进行尾插 就只是将新的节点连接到链表的尾节点之后 那么如果链表没有节点 就没有尾节点 自然也就不能…

网络编程 (一)网络协议TCP,UDP

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1f380;计算机网络概述&#x1f380;网络编程&#x1f380;那么是如何精确找到计算机网络中的目标主机呢&#xff1f;&#x1f380;网络模型&#x1f380;TCP协议&#x1f380;U…