libcurl是C++简单爬虫唯一靠谱起点,需正确初始化、设超时、用回调获取响应体,避免手写正则解析HTML,注意线程安全、UA伪装和反爬细节。

用 libcurl 发起 HTTP 请求是最直接的路
别碰原始 socket 写 HTTP,也别幻想用 std::filesystem 或 std::net(C++23 的 <net></net> 还没实装、不可用)。真实项目里,libcurl 是 C++ 做简单爬虫唯一靠谱的起点——它跨平台、稳定、支持重定向和 cookie,而且错误码清晰。
常见错误现象:curl_easy_perform() 返回 CURLE_COULDNT_RESOLVE_HOST 却以为是代码写错了;其实是 DNS 没通,不是 curl 配置问题。
- 必须调用
curl_global_init(CURL_GLOBAL_DEFAULT)一次,通常在main()开头 -
curl_easy_setopt(handle, CURLOPT_URL, "https://example.com")的 URL 必须是 C 风格字符串(const char*),传std::string.c_str()要确保生命周期够长 - 想拿到响应体?得自己提供回调函数给
CURLOPT_WRITEFUNCTION,并用CURLOPT_WRITEDATA传入目标容器指针(比如std::string*) - 超时一定要设:
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 10L),否则 DNS 卡住会挂死整程序
解析 HTML 别手写正则,用 libxml2 或 htmlcxx
正则匹配 <a href="..."> 看似简单,但遇到换行、属性顺序变化、注释、CDATA 就崩。真实网页里这些全都有。
使用场景:提取链接、标题、正文段落——这些结构化操作交给解析器更稳。
立即学习“C++免费学习笔记(深入)”;
-
libxml2更成熟,但 C 风格 API 略重;初始化要xmlInitParser(),文档加载后记得xmlFreeDoc() -
htmlcxx是轻量 C++ 封装,直接htmlcxx::HTML::ParserDom parser;+parser.parseTree(html_str),适合快速原型 - 两者都不自动处理 JS 渲染内容——如果目标页面靠
fetch()加载数据,libcurl拿到的只是骨架 HTML,不是最终看到的页面 - 编码注意:
libxml2默认按 UTF-8 解析,若网页是 GBK,需先用iconv转码,否则中文变乱码
std::thread 并发请求前先确认 libcurl 是线程安全的
很多人一上来就开 10 个 std::thread 各跑一个 curl_easy_*(),结果偶发崩溃或返回空响应——因为没搞清 libcurl 的线程模型。
关键事实:libcurl 的 easy 接口本身是线程安全的,但前提是:每个线程用独立的 CURL* handle;全局初始化(curl_global_init)只做一次;且不能共享 handle 或 share 对象。
- 绝对不要在多个线程间传递或复用同一个
CURL*句柄 - 如果要用连接复用(keep-alive),应改用
multi interface(curl_multi_*()),它专为并发设计,比手撸线程+easy 更高效、更可控 - DNS 缓存默认是进程级的,多线程下高并发请求同一域名时,可能因锁竞争拖慢,可考虑关掉:
curl_easy_setopt(h, CURLOPT_DNS_CACHE_TIMEOUT, 0L)
绕过反爬最常被忽略的是 User-Agent 和请求间隔
不是所有网站都封 IP,但绝大多数会拒收 libcurl-agent/1.0 这种默认 UA。没有 UA 的请求,连 403 都不给你,直接断连。
性能影响:过于频繁请求会触发 WAF 限流,比如 Cloudflare 的 “checking your browser” 页面,libcurl 拿不到 HTML,只收到 JS 挑战脚本。
- 必须设置真实 UA:
curl_easy_setopt(h, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36") - 加 Referer 有时必要(尤其图片或接口):
curl_easy_setopt(h, CURLOPT_REFERER, "https://example.com/") - 两次请求之间至少
sleep_for(1s),别用usleep(精度低还容易被调度器打乱) - 别依赖
robots.txt判断能不能爬——它只是建议;但真被封了,先查日志里是不是漏了 UA 或 Cookie
真正麻烦的从来不是“怎么发请求”,而是“怎么让对方相信你是浏览器”。UA、间隔、Headers 顺序、HTTP/1.1 的 Connection 字段……这些细节堆起来,才决定爬虫活多久。









