C语言编写轻量爬虫工具

发布于:2025-08-06 ⋅ 阅读:(13) ⋅ 点赞:(0)

当我们要使用C语言编写一个定制化轻量爬虫工具,得需要结合网络请求、HTML解析和数据处理等步骤。由于是轻量级,正常情况下我们将使用C语言标准库以及一些第三方库来简化开发。这样省时省力,生态丰富可以帮助大家少走很多弯路。具体细节可以看下面具体细节。

在这里插入图片描述

下面是一个分步指南和示例代码:

核心组件

1、网络请求:使用libcurl库获取网页内容

2、HTML解析:使用libxml2解析HTML

3、数据存储:将结果保存到文件或内存

4、定制规则:通过回调函数实现定制逻辑

完整示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>

// 存储HTTP响应数据
struct MemoryStruct {
    char *memory;
    size_t size;
};

// libcurl回调函数
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)userp;

    char *ptr = realloc(mem->memory, mem->size + realsize + 1);
    if(!ptr) {
        fprintf(stderr, "内存分配失败\n");
        return 0;
    }

    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;

    return realsize;
}

// 提取链接的回调函数类型定义
typedef void (*LinkHandler)(const char* url, void* userdata);

// 核心爬取函数
void crawl_page(const char* url, LinkHandler handler, void* userdata) {
    CURL *curl;
    CURLcode res;
    struct MemoryStruct chunk = {0};

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    
    if(curl) {
        // 设置cURL选项
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; MyCrawler/1.0)");
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);

        // 执行请求
        res = curl_easy_perform(curl);
        if(res != CURLE_OK) {
            fprintf(stderr, "请求失败: %s\n", curl_easy_strerror(res));
        } else {
            // 解析HTML文档
            htmlDocPtr doc = htmlReadDoc((xmlChar*)chunk.memory, url, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
            if (doc) {
                xmlXPathContextPtr context = xmlXPathNewContext(doc);
                if (context) {
                    // 查找所有<a>标签
                    xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)"//a/@href", context);
                    if (result) {
                        xmlNodeSetPtr nodeset = result->nodesetval;
                        for (int i = 0; i < nodeset->nodeNr; i++) {
                            xmlChar *href = xmlNodeListGetString(doc, nodeset->nodeTab[i]->children, 1);
                            if (handler && href) {
                                handler((const char*)href, userdata);
                            }
                            xmlFree(href);
                        }
                        xmlXPathFreeObject(result);
                    }
                    xmlXPathFreeContext(context);
                }
                xmlFreeDoc(doc);
            }
        }
        curl_easy_cleanup(curl);
    }
    
    free(chunk.memory);
    curl_global_cleanup();
}

// 示例链接处理函数
void print_links(const char* url, void* userdata) {
    FILE *output = (FILE*)userdata;
    fprintf(output, "发现链接: %s\n", url);
}

// 主函数
int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "用法: %s <目标URL> <输出文件>\n", argv[0]);
        return 1;
    }

    FILE *output = fopen(argv[2], "w");
    if (!output) {
        perror("无法打开输出文件");
        return 1;
    }

    fprintf(output, "爬取结果: %s\n", argv[1]);
    crawl_page(argv[1], print_links, output);

    fclose(output);
    printf("爬取完成!结果已保存至 %s\n", argv[2]);
    return 0;
}

编译与运行

1、安装依赖库

# Ubuntu/Debian
sudo apt-get install libcurl4-openssl-dev libxml2-dev

2、编译程序

gcc -o mycrawler mycrawler.c -lcurl -lxml2

3、运行示例

./mycrawler https://example.com output.txt

定制化扩展方向

1、过滤特定链接

void filter_links(const char* url, void* userdata) {
    if (strstr(url, ".pdf")) {
        printf("跳过PDF文件: %s\n", url);
        return;
    }
    // 其他处理...
}

2、限制爬取深度

// 添加depth参数到crawl_page函数
void crawl_page(const char* url, LinkHandler handler, void* userdata, int depth) {
    if (depth <= 0) return;
    // ...获取页面内容...
    // 递归爬取子链接
    crawl_page(new_url, handler, userdata, depth-1);
}

3、数据提取

// 修改XPath表达式提取其他数据
xmlXPathEvalExpression((xmlChar*)"//h1/text()", context);

4、多线程支持

  • 使用pthread库创建线程池
  • 实现线程安全的队列管理URL

敲黑板,看重点

1、遵守robots.txt:在爬取前检查目标网站的爬取规则

2、设置延迟:避免高频请求导致IP被封

sleep(1); // 每次请求后暂停1秒

3、错误处理:添加HTTP状态码检查

long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200) { /* 处理错误 */ }

4、内存管理:确保释放所有分配的内存

这个爬虫框架约150行代码,可根据自身项目需求进一步扩展功能。保持轻量化的关键在于专注于核心功能,避免不必要的特性。如果大家跑编程的时候遇到问题可以截图留言讨论。


网站公告

今日签到

点亮在社区的每一天
去签到