批量请求Kijiji页面遇异常响应,疑遭反爬?求排查方案
问题背景
开发程序通过批量GET请求从Kijiji获取约50个广告的HTML用于统计分析,遇到以下异常:
- 前30-40次请求返回正常结果,后续请求返回异常HTML(浏览器打开为无限刷新的空页面)
- 大量异常响应后会出现1-3次正常响应;将总请求数减至30时,15次正常请求后开始异常,末尾仍有1-3次正常响应
- 手动分步执行(加断点)时所有请求均正常,仅循环自动执行时触发问题
- 少量请求(5-10次)时程序运行正常,批量请求时出现异常
现有libcurl处理代码
extern size_t write_data(void* ptr, size_t size, size_t nmemb, void* stream) { int written = fwrite(ptr, size, nmemb, (FILE*)stream); return written; } int txt_Upd(string myURL, int destination) { CURL* curl_handle; static const char* headerfilename; CURLcode res; switch (destination){//create different files so we don't have to refresh the same one back and forth case 1: { headerfilename = "head.txt"; break; } case 2: { headerfilename = "sr.txt"; break; } case 3: { headerfilename = "aux1.txt"; break; } } FILE* headerfile; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, myURL.c_str()); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "VZM_Analytix"); curl_easy_setopt(curl_handle, CURLOPT_HEADER, 1L); curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, "cookies.txt"); //curl_easy_setopt(curl_handle, CURLOPT_COOKIEJAR, "cookies.txt"); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, TRUE); curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data); headerfile = fopen(headerfilename, "w"); if (headerfile == NULL) { curl_easy_cleanup(curl_handle); return 2; } curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, headerfile); res = curl_easy_perform(curl_handle); fclose(headerfile); curl_easy_cleanup(curl_handle); return res; }
排查思路
1. 处理请求频率限制
Kijiji存在反爬机制,循环快速请求会触发限流,手动分步执行因间隔长未触发。可:
- 在每次请求后添加1-3秒的随机延迟,模拟人类浏览间隔
- 记录请求时间戳,确认异常出现时的请求间隔是否过短
2. 修复libcurl全局初始化问题
当前代码每次调用txt_Upd都执行curl_global_init(CURL_GLOBAL_ALL),该函数仅需在程序启动时调用一次,重复调用会导致资源泄漏或异常。将其移至程序入口处,不要放在请求函数内。
3. 启用Cookie持久化
当前仅设置CURLOPT_COOKIEFILE(读取Cookie),注释掉了CURLOPT_COOKIEJAR(保存Cookie)。Kijiji可能通过Cookie识别会话,未持久化Cookie会导致每次请求被当作新会话,易触发限制。取消注释CURLOPT_COOKIEJAR,确保Cookie在请求间持续有效。
4. 检查HTTP响应状态码
在curl_easy_perform后,调用curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code)获取响应状态码,异常时可能返回429(请求过多)、503等状态,通过状态码确认是否被限流。
5. 模拟真实浏览器请求头
当前仅设置User-Agent,真实浏览器会发送更多请求头(如Accept、Accept-Language、Referer等)。添加这些头信息,让请求更接近正常浏览器:
struct curl_slist* headers = NULL; headers = curl_slist_append(headers, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); headers = curl_slist_append(headers, "Accept-Language: en-US,en;q=0.5"); headers = curl_slist_append(headers, "Referer: https://www.kijiji.ca/"); curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); // 记得在curl_easy_cleanup前释放headers curl_slist_free_all(headers);
6. 排查文件写入冲突
当前destination对应3个固定文件,若循环中重复使用同一destination,会导致文件内容混乱,看起来像异常响应。确保每次请求使用独立文件名,或确认destination参数在循环中分配正确。
7. 启用libcurl调试日志
添加CURLOPT_VERBOSE选项,输出详细请求和响应日志,查看异常请求的通信细节:
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
日志会显示请求头、响应头、连接状态等信息,帮助定位是请求被拦截还是响应异常。
内容的提问来源于stack exchange,提问作者Vroom ZOOM

