首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP CURL_MULTI 多線程采集實戰:效率提升 8 倍的遠程抓取封裝類

     編輯:PHP 綜合知識

實際項目中需下載大量圖片/頁面,單線程 foreach + curl_exec 或 file_get_contents 效率極低。本文提供封裝好的 remote() 函數,實測抓取 10 頁面:**curl_multi 4.5 秒 vs file_get_contents 33 秒,效率提升 7.3 倍**。

完整封裝代碼

<?php
/**
 * 並發抓取多個 URL
 * @param array $urls 目標 URL 數組
 * @return array|false 成功返回 [url => content],失敗返回 false
 */
function remote(array $urls)
{
    if (empty($urls)) return false;

    $curl = $text = [];
    $mh = curl_multi_init();

    foreach ($urls as $k => $v) {
        // URL 中文編碼處理(僅路徑部分)
        $nurl = preg_replace_callback('~([^:/\\.]+)~', function ($m) {
            return rawurlencode($m[1]);
        }, $v);

        $curl[$k] = curl_init($nurl);
        curl_setopt($curl[$k], CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl[$k], CURLOPT_HEADER, 0);
        curl_setopt($curl[$k], CURLOPT_TIMEOUT, 15);       // 總超時
        curl_setopt($curl[$k], CURLOPT_CONNECTTIMEOUT, 5); // 連接超時
        curl_multi_add_handle($mh, $curl[$k]);
    }

    $active = null;
    // 驅動階段
    do {
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    // 事件循環階段
    while ($active && $mrc == CURLM_OK) {
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }

    // 收集結果
    foreach ($curl as $k => $ch) {
        if (curl_error($ch) === '') {
            $text[$k] = (string)curl_multi_getcontent($ch);
        } else {
            $text[$k] = ['error' => curl_error($ch), 'code' => curl_errno($ch)];
        }
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);
    return $text;
}

// 測試
$text = remote(['https://www.jb51.net/', 'https://www.baidu.com/']);
print_r($text);
?>

關鍵優化點

  1. URL 編碼安全: preg_replace_callback 僅編碼路徑段非保留字符,保留協議、域名、端口。
  2. 超時雙控: CURLOPT_TIMEOUT + CURLOPT_CONNECTTIMEOUT 防止單個慢請求阻塞整批。
  3. 錯誤隔離: 單個請求失敗不影響其他,返回錯誤信息而非拋出異常。
  4. 內存友好: 及時 curl_close 釋放句柄資源。

性能對比數據(作者實測)

方式耗時(秒)倍數
curl_multi (並發)4.521x (基準)
file_get_contents (串行)33.007.3x 慢
curl 串行 foreach~30+接近 file_get_contents

擴展建議

  • 大規模采集(萬級+):引入 **Guzzle Promise** / **amphp** / **Swoole 協程** 實現更高併發控制(限流、重試、代理池)。
  • 斷點續傳:記錄已成功 URL 到 Redis/文件,重啟時跳過。
  • 反反爬蟲:隨機 User-Agent、IP 代理池、請求間隔抖動、Cookie 池輪換。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved