百万级外链图片批量下载最优方案咨询(含PHP/JS限制)
批量下载130万+重定向图片的最优方案
针对你遇到的原链接重定向到临时URL、PHP单线程下载效率极低的问题,以下是几个可落地的高效解决方案:
方案一:PHP多进程并行下载
利用PHP的多进程扩展提升并发量,直接缩短总耗时:
- 核心思路:通过
pcntl_fork()创建子进程,同时处理多个下载任务(比如一次启动50-100个进程,根据服务器配置调整),将单张2秒的耗时摊薄,总耗时可从30天压缩至十几小时。 - 关键细节:加入失败重试机制,记录下载失败的链接后续单独处理;设置请求超时,避免进程长时间挂起;控制并发数,防止触发目标服务器限流。
示例代码框架:
$imageUrls = []; // 从数据库取出的所有图片链接 $maxProcesses = 50; $processes = []; $saveDir = './downloads/'; @mkdir($saveDir, 0777, true); foreach ($imageUrls as $url) { // 等待进程数降到阈值以下 while (count($processes) >= $maxProcesses) { foreach ($processes as $i => $pid) { if (pcntl_waitpid($pid, $status, WNOHANG)) { unset($processes[$i]); } } usleep(10000); } $pid = pcntl_fork(); if ($pid == -1) { die("无法创建子进程"); } elseif ($pid == 0) { // 子进程处理下载 $context = stream_context_create([ 'http' => [ 'follow_location' => true, 'timeout' => 10, 'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' ] ]); try { $imageData = file_get_contents($url, false, $context); if ($imageData) { $filename = md5($url) . '.jpg'; file_put_contents($saveDir . $filename, $imageData); } else { file_put_contents('./failed.txt', $url . "\n", FILE_APPEND); } } catch (Exception $e) { file_put_contents('./failed.txt', $url . ' | ' . $e->getMessage() . "\n", FILE_APPEND); } exit(0); } else { $processes[] = $pid; } } // 等待所有子进程结束 foreach ($processes as $pid) { pcntl_waitpid($pid, $status); }
方案二:命令行工具批量下载(推荐)
用wget或curl这类优化成熟的命令行工具,比PHP代码更高效,操作更简单:
1. 使用wget
- 步骤:先把数据库中的所有链接导出到
image_urls.txt(每行一个链接),再执行命令:
wget --input-file=image_urls.txt --directory-prefix=./downloads --continue --background --wait=0.1 --random-wait --follow-tries=3 --max-redirect=5 --limit-rate=5M --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
参数说明:
--input-file:指定URL列表文件--directory-prefix:设置保存目录--wait/--random-wait:添加请求间隔,避免触发限流--follow-tries:重定向失败后的重试次数--limit-rate:限制下载速率,防止占满服务器带宽
2. 使用curl + xargs
- 命令:
xargs -n 1 -P 50 curl -L -o "./downloads/$(md5sum {} | cut -d' ' -f1).jpg" --retry 3 --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" < image_urls.txt
参数说明:
-P 50:同时启动50个curl进程-L:自动跟随重定向--retry 3:失败重试3次$(md5sum {} | cut -d' ' -f1):用URL哈希值作为文件名,避免重复或非法字符
方案三:Chrome HAR文件提取(仅适合小批量验证)
如果仅需测试部分图片,可通过Chrome生成的HAR文件提取base64格式的图片:
- 打开Chrome开发者工具(F12),切换到Network面板,勾选「Preserve log」
- 加载目标页面或批量打开图片链接,待所有图片加载完成后,右键请求列表选择「Save all as HAR with content」保存文件
- 用PHP解析HAR文件提取图片:
$harContent = file_get_contents('./images.har'); $harData = json_decode($harContent, true); $saveDir = './extracted/'; @mkdir($saveDir, 0777, true); foreach ($harData['log']['entries'] as $entry) { if (strpos($entry['response']['content']['mimeType'], 'image/') === 0) { if ($entry['response']['content']['encoding'] == 'base64') { $imageData = base64_decode($entry['response']['content']['text']); $filename = md5($entry['request']['url']) . '.jpg'; file_put_contents($saveDir . $filename, $imageData); } } }
注意:该方法不适合130万+的批量处理,Chrome一次性加载大量链接会崩溃,且HAR文件体积过大,解析效率极低。
通用避坑提示
- 控制并发数:先从10-20的低并发测试,逐步调整到服务器和目标站点能承受的数值,避免IP被封禁
- 失败重试:必须记录下载失败的链接,后续单独处理,避免遗漏图片
- 文件名规范:用URL的哈希值作为文件名,避免重复或非法字符导致保存失败
- 模拟合法UA:设置符合浏览器规范的User-Agent,降低被目标站点拦截的概率
内容的提问来源于stack exchange,提问作者Daniel Bengtsson
相关产品推荐
相关产品推荐

