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

PHP 採集遠端圖片並本地化儲存:正則提取、cURL 下載、路徑替換完整方案

     編輯:PHP 綜合知識

內容採集時常需將遠端圖片下載至本地伺服器,避免防盜鏈、提升載入速度、確保資料永久保存。本文提供一套完整方案:支援 cURL/fopen 雙模式下載、自動建立日期目錄、檔名衝突處理、下載失敗佔位圖、HTML 圖片路徑批次替換。

核心功能模組

  1. 通用下載函式 my_file_get_contents():優先使用 cURL,回退 file_get_contents,支援超時設定
  2. 目錄建立 mkdirs():遞迴建立多層目錄,回傳布林結果
  3. 主處理函式 get_remote():正則提取 src、下載、儲存、替換 HTML 路徑

完整程式碼實作

<?php
/**
 * 通用遠端內容獲取 (支援 cURL / fopen)
 * @param string $url
 * @param int $timeout 秒
 * @return string|false
 */
function my_file_get_contents($url, $timeout = 30) {
    if (function_exists('curl_init')) {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; PHP Image Collector)',
            CURLOPT_SSL_VERIFYPEER => false,  // 採集場景常關閉驗證
        ]);
        $content = curl_exec($ch);
        curl_close($ch);
        return $content;
    }
    if (ini_get('allow_url_fopen')) {
        $ctx = stream_context_create(['http' => ['timeout' => $timeout]]);
        return @file_get_contents($url, false, $ctx);
    }
    return false;
}

/**
 * 遞迴建立目錄
 * @param string $dir
 * @return bool
 */
function mkdirs($dir) {
    if (is_dir($dir)) return true;
    if (mkdirs(dirname($dir)) && mkdir($dir, 0777, true)) return true;
    return false;
}

/**
 * 採集內容中的遠端圖片並本地化
 * @param string $body  HTML 內容
 * @param string $title 標題 (用於生成檔名鹽值)
 * @return string 處理後的 HTML 內容
 */
function get_remote($body, $title = '') {
    $imgArray = [];
    $basePath = realpath(__DIR__ . "/../../../upfile/news/") . '/';
    $dateDir = date("Y/m/d/");
    $saveDir = $basePath . $dateDir;
    $visitPath = '/upfile/news/' . $dateDir;  // 前端存取路徑

    $body = stripslashes(strtolower($body));

    // 提取所有  的網址 (支援單雙引號、無引號)
    preg_match_all('/(src|SRC)\s*=\s*["\']?\s*(https?:\/\/[^\s"\'>]+\.(gif|jpg|jpeg|png))/isu', $body, $matches);
    $imgUrls = array_unique($matches[2] ?? []);

    foreach ($imgUrls as $url) {
        $content = my_file_get_contents($url, 60);
        if (empty($content)) {
            // 重試一次
            sleep(10);
            $content = my_file_get_contents($url, 30);
        }

        if (empty($content)) {
            // 下載失敗:替換為佔位圖
            $body = preg_replace('#' . preg_quote($url, '#') . '#isu', '/notfound.jpg', $body);
            continue;
        }

        // 生成唯一檔名:年月日時分秒 + 隨機數 + 副檔名
        $ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
        $ext = in_array($ext, ['gif','jpg','jpeg','png']) ? $ext : 'jpg';
        $filename = date('YmdHis') . rand(1000, 9999) . '.' . $ext;

        // 確保目錄存在並寫入
        if (mkdirs($saveDir)) {
            $fp = fopen($saveDir . $filename, 'wb');
            if ($fp && fwrite($fp, $content)) {
                fclose($fp);
                // 替換 HTML 中的圖片路徑
                $body = preg_replace('#' . preg_quote($url, '#') . '#isu', $visitPath . $filename, $body);
            } else {
                if ($fp) fclose($fp);
            }
        }
        // 禮貌延遲,避免對來源站造成壓力
        sleep(6);
    }

    // 修正可能被壓縮的標籤
    $body = str_replace(' ...更多內容
?>

關鍵細節解析

環節處理方式目的
下載器選擇優先 cURL,其次 fopen相容不同主機環境,cURL 功能更完整
SSL 驗證CURLOPT_SSL_VERIFYPEER => false採集 HTTPS 站點時避免憑證錯誤中斷
User-Agent模擬瀏覽器 UA降低被反爬蟲攔截機率
檔名生成時間戳 + 隨機數 + 原副檔名避免衝突、可追溯時間、保留格式
目錄結構年/月/日 三層單目錄檔案過多導致 inode 耗盡、列表緩慢
失敗處理重試 1 次 → 替換佔位圖不中斷整體採集流程、保持版面完整
路徑替換preg_replace + preg_quote精確替換、避免特殊字元干擾正則
頻率控制sleep(6)禮貌採集、降低被封 IP 風險

生產環境強化建議

  • 佇列化: 將圖片下載任務推入 Redis/RabbitMQ 佇列,Worker 非同步處理,採集主流程秒級回應
  • 去重機制: 以圖片 URL MD5 為 Key 存入 Redis/資料庫,避免重複下載同一張圖
  • 圖片優化: 下載後經 imagemin/mozjpeg/webp 壓縮,節省儲存與頻寬
  • CDN 上傳: 儲存完成同步上傳至 OSS/S3/CDN,前端直接走 CDN 域名
  • 版權合規: 保留來源站版權資訊、遵守 robots.txt、提供下架管道
  • 監控告警: 記錄下載成功率、耗時、失敗原因,異常時自動通知

常見坑點提醒

  • 正則未涵蓋 data-src、data-original 等延遲載入屬性,需視來源站結構擴充
  • 相對路徑圖片 (如 /images/a.jpg) 需先補全為絕對 URL 再下載
  • preg_replace 替換時若同一張圖在頁面出現多次,會全部被替換 (通常為預期行為)
  • 大量採集時 sleep() 會嚴重拖慢總耗時,務必改用非同步佇列
  • Windows 環境下 realpath() 與路徑分隔符需注意相容性

將此方案模組化封裝為 Composer 套件,即可在多專案中複用,並持續迭代優化下載器、儲存策略與替換邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved