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

前言:这学期计算机软件课程设计的其中一个题目是使用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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GOOGLE|只有大模型才能理解你举的例子(In-context learning)是什么

一、概述 title&#xff1a;LARGER LANGUAGE MODELS DO IN-CONTEXT LEARNING DIFFERENTLY 论文地址&#xff1a;https://arxiv.org/abs/2303.03846 参考&#xff1a;https://www.xiaohongshu.com/user/profile/5f01057f0000000001003c91/640aa237000000001303d871 1.1 Moti…

springboot基于vue的地方美食分享网站

开发技术介绍 Java介绍 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#xff0c;便于结构的分离&…

Python文件上传 S3(AWS) 简单实现

1.AWS设置 建立aws账户&#xff0c;进入到S3界面 点击 "Create bucket" 一系列操作之后——这里给bucket命名为csfyp 2. Python部分 python需要先&#xff1a; pip install loguru pip install boto3 这两个包含一些连接python和s3 连接的api 然后直接上代码…

Redis学习---05

一、Redis集群搭建&#xff0c;Redis主从复制&#xff0c;读写分离 默认情况下每台redis服务器都是主节点。 (1) 主从复制&#xff1a;是指将一台redis服务器的数据&#xff0c;复制道其他redis服务。前者成为主节点&#xff0c;后者成为从节点。默认情况下每一台redis服务器…

puppeteer-不需重构,无痛加强vue单页面应用的SEO,提升百度收录排名

背景 最近产品觉得我们网站在百度收录上排名太靠后了&#xff0c;又不肯花钱&#xff0c;就让我们想办法提升网站的SEO。由于项目是用vue3写的&#xff0c;并且已经迭代多个版本了&#xff0c;用nuxt实在不适宜&#xff0c;当然俺的开发水平也不够&#xff0c;周期也会拉得很长…

【华为机试】——每日刷题经验分享

【华为机试】——每日刷题经验分享&#x1f60e; 前言&#x1f64c;题目&#xff1a;HJ9 提取不重复的整数 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &a…

LabVIEWCompactRIO 开发指南22 CVT客户端通信(CCC)

LabVIEWCompactRIO 开发指南22 CVT客户端通信&#xff08;CCC&#xff09; 如果使用第3章中讨论的CVT进行进程间通信&#xff0c;请考虑使用CCC。如果已经创建了CVT标签&#xff0c;并且想在网络上发布此数据&#xff0c;CCC不失为一个简单而优雅的解决方案。它基于TCP/IP&am…

Linux 指令3

文章目录 标题日期date时间戳 cal 日历find -name 查找which ls 搜指令whereisgrep 行文本过滤工具&#xff08;例如找到main函数入口&#xff09;用途例子 ps ajx 进程 打包压缩&#xff0c;解包解压&#xff08;过程是这么个过程&#xff0c;简化成压缩->解压&#xff09;…

Java进阶-面向对象进阶(多态包权限修饰符代码块)

1 多态 1.1 多态的形式 多态是继封装、继承之后&#xff0c;面向对象的第三大特性。 多态是出现在继承或者实现关系中的。 多态体现的格式&#xff1a; 父类类型 变量名 new 子类/实现类构造器(); 变量名.方法名();多态的前提&#xff1a;有继承关系&#xff0c;子类对象…

MySQL高级语句(三)

一、正则表达式&#xff08;REGEXP&#xff09; 1、正则表达式匹配符 字符解释举列^匹配文本的开始字符’ ^aa ’ 匹配以 aa 开头的字符串$匹配文本的结束字符’ aa$ ’ 匹配以aa结尾的字符串.匹配任何单个字符’ a.b 匹配任何a和b之间有一个字符的字符串*匹配零个或多个在它…

MHA高可用与故障切换

一、MHA的概述 1、 MHA的概念 MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故…

前端需要注意和了解的SEO

SEO的基本了解 1.什么是SEO? SEO&#xff08;Search Engine Optimization又叫做搜索引擎优化。是一种方式&#xff1a;利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。 2. 前端怎么理解SEO? 对于SEO引擎&#xff0c;在前端需要的是做出来的网站&#xff0c;页面…

揭秘物联网平台设备管理核心!Java代码示例对比,一篇文章全知道!

《高并发系统实战派》-- 值得拥有 一、 设备管理模块的意义 设备管理模块是物联网平台的核心模块之一&#xff0c;主要负责设备的接入、注册、管理、监控等工作&#xff0c;是构建物联网平台的基础。通过设备管理模块&#xff0c;可以实现对设备的资源动态管理、设备状态实时…

服务(第二十一篇)mysql高级查询语句(二)

①视图表&#xff1a; 视图表是虚拟表&#xff0c;用来存储SQL语句的定义 如果视图表和原表的字段相同&#xff0c;是可以进行数据修改的&#xff1b; 如果两者的字段不通&#xff0c;不可以修改数据。 语法&#xff1a; 创建&#xff1a;create view 试图表名 as ... 查…

vue3项目搭建超详解

vue3安装与目录讲解 文章目录 vue3安装与目录讲解安装node.jsnpm绑定淘宝镜像安装vue脚手架创建vue项目目录解释推荐使用vscode 安装node.js http://nodejs.cn/download/ 根据自己电脑的位数自行下载。可安装到任意盘哈&#xff0c;因为我C盘比较大&#xff0c;我就直接在C盘了…

springboot项目如何优雅停机

文章目录 前言kill -9 pid的危害如何优雅的停机理论步骤优雅方式1、kill -15 pid 命令停机2、ApplicationContext close停机3、actuator shutdown 停机4、ApplicationListener 监听延时停机 前言 相信很多同学都会用Kill -9 PID来杀死进程&#xff0c;如果用在我们微服务项目里…

快速入门matlab——变量练习

学习目标&#xff1a;1.掌握matlab编程中最常用的几种变量类型 2.对变量类型的属性有所熟悉&#xff0c;不要求记忆&#xff0c;知道了解即可 3.要求熟练运用这几种变量类型创建自己的变量 clear all; % 清除Workspace中的所有…

FreeRTOS_移植和配置

目录 1. 什么是FreeRTOS&#xff1f; 2. FreeRTOS 特点 3. FreeRTOS 移植 3.1 验证程序 1. 什么是FreeRTOS&#xff1f; 我们先看 FreeRTOS 的名字&#xff0c;可以分成两部分&#xff1a;Free 和 RTOS&#xff0c;Free 就是免费的、自由的、不受约束的意思&#xff0c;RTO…

ERP、SCM与CRM系统的关系和区别是什么?

在当今数字化时代&#xff0c;企业管理系统扮演着至关重要的角色&#xff0c;而ERP、SCM和CRM系统是其中三个核心组成部分。 虽然它们都在企业管理中发挥着关键作用&#xff0c;但它们各自的功能和应用领域存在一些区别。 我们先来看看&#xff0c;ERP、SCM与CRM系统分别是啥…

DevExpress:报表控件绑定数据库数据源的三种方式(Winform)

1.写在前面 如果你是和我一样&#xff0c;第一次接触DevExpress&#xff0c;并且因为网上资源眼花缭乱无从下手&#xff0c;然后脑子一转直接到DevExpress官网寻找官方使用文档的&#xff0c;那我们的了解顺序应该差不多是一致的。 DevExpress官网&#xff1a;https://www.de…