php批量抓取网页必须用curl多句柄并发,因file_get_contents()仅支持串行;curl_multi_init()是唯一原生并发方案,需手动管理句柄、轮询状态、读取结果并关闭句柄,否则内存泄漏或无法获取响应。

PHP 本身没有内置的“批量抓取网页”功能,file_get_contents() 或 cURL 都是单请求模型;真要并发抓多个页面,必须自己组织并发逻辑,否则就是串行——慢、易超时、服务器可能封 IP。
用 cURL 多句柄实现真正并发请求
PHP 的 curl_multi_init() 是唯一原生支持多 URL 并发发起 HTTP 请求的机制,它不依赖扩展,但需要手动管理句柄、轮询状态、处理错误。
- 每个 URL 对应一个
cURL句柄,统一塞进curl_multi_init()管理器 - 用
curl_multi_exec()启动并持续轮询,直到全部完成(不能只调一次) - 必须配合
curl_multi_info_read()拿到已完成请求的结果,否则拿不到响应体 - 记得对每个句柄调
curl_close(),否则内存泄漏
示例关键片段:
foreach ($urls as $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_multi_add_handle($mh, $ch);
}
while (($exec = curl_multi_exec($mh, $running)) === CURLM_CALL_MULTI_PERFORM);
while ($running && $exec === CURLM_OK) {
if (curl_multi_select($mh) !== -1) {
do {
$info = curl_multi_info_read($mh);
if ($info && $info['result'] === CURLE_OK) {
$content = curl_multi_getcontent($info['handle']);
// 处理 $content
}
curl_multi_remove_handle($mh, $info['handle']);
curl_close($info['handle']);
} while ($info);
}
}
file_get_contents() + stream_context_create() 不适合批量
虽然能用 stream_context_create() 设置超时、UA,但它是同步阻塞的:请求 A 没返回,B 就卡住。哪怕用 set_time_limit(0),也解决不了本质问题。
立即学习“PHP免费学习笔记(深入)”;
- 5 个页面平均 2 秒响应 → 串行就要 10 秒;并发可压到 2–3 秒内
- 超时设置对单个请求有效,但无法避免整体耗时线性增长
- 没错误隔离:一个 502 就可能导致后续请求全被跳过(除非你手动 try/catch 每次)
别忽略 DNS、连接、SSL 开销和反爬策略
真实环境中,慢往往不出现在内容下载阶段,而出现在握手环节。尤其批量请求同一域名时,DNS 查询和 TCP/TLS 握手会成为瓶颈。
- 加
CURLOPT_TCP_NODELAY和复用连接(CURLOPT_FORBID_REUSE设为 false)能明显提升吞吐 - 用
CURLOPT_RESOLVE手动注入 DNS 缓存,绕过系统 DNS 查询(适合固定目标域名) - 多数网站会限制 User-Agent、Referer、请求频率;不带头或间隔太短,直接返回 403 或空页
- 如果目标页含 JavaScript 渲染内容,
cURL拿不到,得换 Puppeteer/Playwright,PHP 仅作调度
真正难的不是发请求,而是控制节奏、解析响应、重试失败、降频避封——这些逻辑一旦漏掉,脚本上线后就只是个定时报错器。











