如何优化PHP双重foreach循环的URL检索脚本以降低服务器负载?
PHP批量页面检查脚本优化方案
先修正原代码的致命bug
原file_get_contents_curl函数中存在参数错误:curl_setopt($curl, CURLOPT_USERAGENT, ...)里的$curl应为$ch,否则User-Agent设置无效,还会产生PHP警告,增加不必要的资源消耗。
核心优化措施
1. 复用Curl句柄,减少连接开销
每次新建Curl连接都会经历TCP三次握手,复用同一句柄能避免重复的连接建立/销毁开销,大幅提升请求效率。
修改后的请求函数:
function get_page_content($url, $ch) { curl_setopt($ch, CURLOPT_URL, $url); $data = curl_exec($ch); return $data; } // 初始化一次Curl句柄,全局复用 $ch = curl_init(); $useragent = 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0'; curl_setopt($ch, CURLOPT_USERAGENT, $useragent); curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 开启持久化连接,进一步降低连接成本 curl_setopt($ch, CURLOPT_KEEPALIVE, true); curl_setopt($ch, CURLOPT_TCP_KEEPIDLE, 60); curl_setopt($ch, CURLOPT_TCP_KEEPINTVL, 60); // 添加超时设置,避免无响应请求占用资源 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 20); // 遍历请求站点 foreach ($sites as $site){ $homepage = get_page_content($site, $ch); if ($homepage === false) { error_log("请求失败:$site"); continue; } // 后续URL检查逻辑 } // 最后关闭句柄 curl_close($ch);
2. 用Curl Multi实现并行请求
同步逐个请求会导致服务器长时间处于阻塞等待状态,使用Curl Multi同时发起多个请求,能大幅缩短总执行时间,降低服务器负载。
示例代码:
function fetch_multiple_pages($sites) { $multiHandle = curl_multi_init(); $handles = []; $useragent = 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0'; // 初始化所有请求句柄 foreach ($sites as $site) { $ch = curl_init($site); curl_setopt($ch, CURLOPT_USERAGENT, $useragent); curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 20); curl_multi_add_handle($multiHandle, $ch); $handles[$site] = $ch; } // 执行并行请求 $running = null; do { curl_multi_exec($multiHandle, $running); curl_multi_select($multiHandle); } while ($running > 0); // 收集结果并清理资源 $results = []; foreach ($handles as $site => $ch) { $results[$site] = curl_multi_getcontent($ch); curl_multi_remove_handle($multiHandle, $ch); curl_close($ch); } curl_multi_close($multiHandle); return $results; } // 批量获取所有页面内容 $pageContents = fetch_multiple_pages($sites);
3. 优化URL查找逻辑
原代码对每个页面逐个调用strpos检查所有目标URL,效率低下。将所有目标URL合并为一个正则表达式,一次匹配完成所有检查,减少字符串操作次数。
// 转义URL中的正则特殊字符,避免匹配错误 $escapedUrls = array_map('preg_quote', $urls); // 构建正则表达式,忽略大小写 $urlPattern = '/(' . implode('|', $escapedUrls) . ')/i'; // 遍历页面内容进行检查 foreach ($pageContents as $site => $homepage) { if (!$homepage) continue; if (preg_match($urlPattern, $homepage)) { echo 'true'; } }
4. 限制并发数,避免过载
如果站点数量过多,一次性发起所有请求可能导致服务器端口耗尽或被目标站点封禁。可以分批处理请求,每批控制并发数:
function fetch_pages_in_batches($sites, $batchSize = 10) { $results = []; // 将站点列表拆分为多个批次 $siteBatches = array_chunk($sites, $batchSize); foreach ($siteBatches as $batch) { $batchResults = fetch_multiple_pages($batch); $results = array_merge($results, $batchResults); // 每批请求后短暂休眠,避免过度请求 usleep(500000); // 休眠0.5秒 } return $results; } // 使用分批请求 $pageContents = fetch_pages_in_batches($sites, 10);
5. 本地缓存静态资源
每次运行脚本都从远程读取站点列表和目标URL,若这些文件不频繁更新,可缓存到本地,减少远程请求开销:
$sitesCache = './sites_cache.txt'; $urlsCache = './urls_cache.txt'; $cacheExpire = 3600; // 缓存有效期1小时 // 读取站点列表,优先使用缓存 if (file_exists($sitesCache) && time() - filemtime($sitesCache) < $cacheExpire) { $sites_raw = file($sitesCache); } else { $sites_raw = file('https://earnmoneysafe.com/script/sites.txt'); file_put_contents($sitesCache, implode("\n", $sites_raw)); } $sites = array_map('trim', $sites_raw); // 读取目标URL,同理使用缓存 if (file_exists($urlsCache) && time() - filemtime($urlsCache) < $cacheExpire) { $urls_raw = file($urlsCache); } else { $urls_raw = file('https://earnmoneysafe.com/script/4toiskatj.txt'); file_put_contents($urlsCache, implode("\n", $urls_raw)); } $urls = array_map('trim', $urls_raw);
内容的提问来源于stack exchange,提问作者Regular User
相关产品推荐
相关产品推荐

