如何用好C语言来做爬虫,想必接触过的大神都能说扥头头是道,但是对于新手小白来说,有这么几点需要注意的。根据设计程序结构,我们需要一个队列来管理待爬取的URL,一个集合或列表来记录已访问的URL。主循环从队列中取出URL,发送请求,解析内容,提取新URL,处理并加入队列。这里需要注意控制并发请求的数量,避免被目标服务器封禁,或者遵守robots.txt,但基础版本可能先不考虑这些,后续可以根据需求像高并发量扩展方向,辅以代理ip能让线程超多爬取。
以下是一个基于C语言实现的简单网络爬虫示例,使用 libcurl 和 libxml2 库完成HTTP请求和HTML解析。该爬虫能够抓取指定起始URL的页面并提取其中的链接。
实现步骤说明
-
依赖库安装
# Ubuntu/Debian sudo apt-get install libcurl4-openssl-dev libxml2-dev
-
代码实现
#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; mem->memory = realloc(mem->memory, mem->size + realsize + 1); if (mem->memory == NULL) { printf("内存分配失败!\n"); return 0; } memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; // Null-terminate return realsize; } // 从HTML中提取所有链接 void extract_links(const char *html_content, const char *base_url) { xmlDoc *doc = htmlReadDoc((const xmlChar*)html_content, base_url, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); if (doc == NULL) return; xmlXPathContext *context = xmlXPathNewContext(doc); if (context == NULL) { xmlFreeDoc(doc); return; } // XPath表达式查找所有<a>标签的href属性 xmlXPathObject *result = xmlXPathEvalExpression((const xmlChar*)"//a/@href", context); if (result == NULL) { xmlXPathFreeContext(context); xmlFreeDoc(doc); return; } // 遍历结果并打印链接 xmlNodeSet *nodeset = result->nodesetval; for (int i = 0; i < nodeset->nodeNr; i++) { xmlChar *href = xmlNodeListGetString(doc, nodeset->nodeTab[i]->children, 1); printf("发现链接: %s\n", href); xmlFree(href); } // 清理资源 xmlXPathFreeObject(result); xmlXPathFreeContext(context); xmlFreeDoc(doc); } // 抓取指定URL的页面内容 void crawl_url(const char *url) { CURL *curl_handle; CURLcode res; struct MemoryStruct chunk; chunk.memory = malloc(1); // 初始化为空 chunk.size = 0; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); // 设置CURL选项 curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "Mozilla/5.0 (兼容性爬虫)"); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); // 跟随重定向 // 执行请求 res = curl_easy_perform(curl_handle); if (res != CURLE_OK) { fprintf(stderr, "请求失败: %s\n", curl_easy_strerror(res)); } else { printf("成功抓取: %s (%zu字节)\n", url, chunk.size); extract_links(chunk.memory, url); // 解析链接 } // 清理资源 curl_easy_cleanup(curl_handle); free(chunk.memory); curl_global_cleanup(); } int main() { const char *start_url = "http://example.com"; // 起始URL crawl_url(start_url); return 0; }
-
编译指令
gcc crawler.c -o crawler -lcurl -lxml2
功能说明
-
HTTP请求处理
- 使用
libcurl
发送GET请求并处理响应。 - 支持自动重定向(
CURLOPT_FOLLOWLOCATION
)。 - 自定义User-Agent避免被简单拦截。
- 使用
-
HTML解析
- 使用
libxml2
的XPath功能提取所有<a>
标签的href
属性。 - 自动处理不规范的HTML(通过
HTML_PARSE_RECOVER
模式)。
- 使用
-
内存管理
- 动态分配内存存储网页内容,使用后释放。
扩展建议
-
URL去重
- 添加哈希表(如
uthash
库)记录已访问的URL,避免重复抓取。
- 添加哈希表(如
-
多线程
- 使用
pthread
实现多线程并发抓取,提升效率。
- 使用
-
遵守Robots协议
- 在抓取前检查目标网站的
/robots.txt
,解析禁止抓取的路径。
- 在抓取前检查目标网站的
-
链接标准化
- 将相对路径转换为绝对URL(例如
/about
→http://example.com/about
)。
- 将相对路径转换为绝对URL(例如
注意事项
- 法律与伦理:确保遵守目标网站的
robots.txt
和服务条款,避免高频请求导致服务器过载。 - 错误处理:代码中省略了部分错误处理以简化示例,实际应用需完善。
- 性能优化:可根据需求添加DNS缓存、连接复用等机制。
上面这个示例我只是给大家提供了一个基础的爬虫框架,具体情况大家可以可根据具体需求进一步扩展功能。