当我们要使用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行代码,可根据自身项目需求进一步扩展功能。保持轻量化的关键在于专注于核心功能,避免不必要的特性。如果大家跑编程的时候遇到问题可以截图留言讨论。