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

PHP 正則提取圖片 URL 進階:排除特定屬性、轉換相對路徑、CDN 域名注入

     編輯:PHP 相關知識

核心需求分析

  • 從 HTML 中提取 <img> 標簽的 src 屬性值
  • 排除包含特定屬性 (如 ico, data-ignore) 的圖片
  • 將相對路徑轉換為絕對 URL (注入當前域名/CDN 域名)
  • 替換原 HTML 中的 src 為新 URL

完整解決方案類

<?php
class ImageUrlProcessor {
    private string $baseUrl;
    private array $excludeAttrs = ['ico', 'data-ignore', 'data-no-cdn'];
    private string $imgRegex;
    
    public function __construct(string $baseUrl = '') {
        $this->baseUrl = rtrim($baseUrl, '/');
        // 正則: 匹配 excludeAttrs));
        $this->imgRegex = '/])*\bsrc=(["\'])(.*?)\1/';
    }
    
    public function setExcludeAttrs(array $attrs): self {
        $this->excludeAttrs = $attrs;
        return $this;
    }
    
    // 提取並轉換 URL
    public function process(string $html): array {
        $result = [
            'html' => $html,
            'images' => [],
            'replaced' => 0,
        ];
        
        $urls = [];
        $patterns = [];
        $replacements = [];
        
        // 1. 找出所有符合條件的 img 標簽及 src
        preg_match_all($this->imgRegex, $html, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
        
        foreach ($matches as $m) {
            $fullTag = $m[0][0];
            $quote = $m[1][0];
            $src = $m[2][0];
            $tagPos = $m[0][1];
            $srcPos = $m[2][1];
            
            // 2. 構建絕對 URL
            $absoluteUrl = $this->toAbsoluteUrl($src);
            
            // 3. 記錄
            $urls[] = [
                'original_src' => $src,
                'absolute_url' => $absoluteUrl,
                'tag' => $fullTag,
                'position' => $srcPos,
            ];
            
            // 4. 準備替換 (按位置倒序替換,避免位置偏移)
            $patterns[] = '/' . preg_quote($quote . $src . $quote, '/') . '/';
            $replacements[] = $quote . $absoluteUrl . $quote;
        }
        
        // 5. 執行替換 (倒序)
        if ($patterns) {
            // 為了精確替換對應位置,使用 preg_replace_callback 配合偏移
            // 簡化版:直接全局替換 (假設 src 值唯一)
            $result['html'] = preg_replace($patterns, $replacements, $html, -1, $count);
            $result['replaced'] = $count;
        }
        
        $result['images'] = $urls;
        return $result;
    }
    
    // 相對路徑轉絕對
    private function toAbsoluteUrl(string $src): string {
        // 已是絕對 URL 或協議相對 URL
        if (preg_match('/^(?:https?:)?\/\//i', $src) || preg_match('/^[a-z]+:/i', $src)) {
            return $src;
        }
        // 根相對路徑 /path
        if (str_starts_with($src, '/')) {
            return $this->baseUrl . $src;
        }
        // 相對路徑 path/to/img.jpg
        return $this->baseUrl . '/' . ltrim($src, '/');
    }
}

// 使用示例
$html = '<div>
    <img ico src="/favicon.ico" />                    <!-- 排除: 有 ico 屬性 -->
    <img src="/images/logo.png" alt="Logo">           <!-- 轉換 -->
    <img data-ignore src="banner.jpg">                <!-- 排除 -->
    <img src="uploads/photo.jpg" class="thumb">       <!-- 轉換 -->
    <img src="https://external.com/ad.jpg">           <!-- 保持不變 -->
</div>';

$processor = new ImageUrlProcessor('https://cdn.myapp.com');
$result = $processor->process($html);

echo "處理後 HTML:\n$result[html]\n\n";
echo "提取圖片:\n";
print_r($result['images']);

輸出結果

處理後 HTML:
<div>
    <img ico src="/favicon.ico" />
    <img src="https://cdn.myapp.com/images/logo.png" alt="Logo">
    <img data-ignore src="banner.jpg">
    <img src="https://cdn.myapp.com/uploads/photo.jpg" class="thumb">
    <img src="https://external.com/ad.jpg">
</div>

提取圖片:
Array
(
    [0] => Array
        (
            [original_src] => /images/logo.png
            [absolute_url] => https://cdn.myapp.com/images/logo.png
            [tag] => Logo
            [position] => 45
        )
    [1] => Array
        (
            [original_src] => uploads/photo.jpg
            [absolute_url] => https://cdn.myapp.com/uploads/photo.jpg
            [tag] => 
            [position] => 132
        )
)

正則核心解析:負向前瞻排除屬性


// 關鍵片段: (?:(?!ico|data-ignore)[^>])*
// 拆解:
// (?: ... )*        非捕獲組,重複 0~n 次
// (?!ico|data-ignore)  負向前瞻:當前位置後面不能是 "ico" 或 "data-ignore"
// [^>]               匹配任意非 > 字符 (即標簽內屬性字符)
// 整體含義: 匹配任意長度的標簽內容,但內容中任意位置都不出現排除詞

DOMDocument 專業版 (推薦用於複雜 HTML)

<?php
function processImagesDom(string $html, string $baseUrl, array $excludeAttrs): array {
    $dom = new DOMDocument();
    libxml_use_internal_errors(true);
    // 關鍵: 加入 XML 聲明防止中文亂碼,LIBXML_HTML_NOIMPLIED 防止自動補全 html/body
    $dom->loadHTML('' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    libxml_clear_errors();
    
    $xpath = new DOMXPath($dom);
    $images = [];
    
    // 查找所有 img
    foreach ($xpath->query('//img') as $img) {
        // 檢查排除屬性
        $excluded = false;
        foreach ($excludeAttrs as $attr) {
            if ($img->hasAttribute($attr)) { $excluded = true; break; }
        }
        if ($excluded) continue;
        
        $src = $img->getAttribute('src');
        if (empty($src)) continue;
        
        // 轉換 URL
        $absoluteUrl = toAbsoluteUrl($src, $baseUrl);
        
        // 記錄
        $images[] = ['original' => $src, 'absolute' => $absoluteUrl];
        
        // 修改 DOM
        $img->setAttribute('src', $absoluteUrl);
    }
    
    // 輸出 HTML (保持原始格式盡可能一致)
    $newHtml = $dom->saveHTML();
    // saveHTML 可能添加 ,若用 NOIMPLIED 則不會
    return ['html' => $newHtml, 'images' => $images];
}

function toAbsoluteUrl(string $src, string $baseUrl): string {
    if (preg_match('/^(?:https?:)?\/\//i', $src) || preg_match('/^[a-z]+:/i', $src)) return $src;
    $baseUrl = rtrim($baseUrl, '/');
    return $baseUrl . (str_starts_with($src, '/') ? $src : '/' . ltrim($src, '/'));
}

性能對比與選型建議

方案速度健壯性功能性適用場景
正則 (preg_replace)極快中 (易受異常 HTML 影響)基礎提取替換結構固定、高性能、簡單邏輯
DOMDocument + XPath中等極高 (標準解析器)完整 DOM 操作複雜 HTML、需修改屬性/結構、生產環境
symfony/dom-crawler中等極高CSS 選擇器、鏈式 API現代項目、採集器、測試

常見坑點避坑

  1. src 值包含引號/特殊字符: 正則用 (["\'])(.*?)\1 捕獲引號並反向引用,比 src=["\']([^"\']*)["\'] 更可靠。
  2. 跨行屬性: 正則加 s 修飾符 (DOTALL) 讓 . 匹配換行,或用 [^>]* 替代 .*?。
  3. 編碼問題: loadHTML 前必加 ,否則中文會被實體化為 &#xxx;。
  4. 遞歸替換位置偏移: 多次替換時按 PREG_OFFSET_CAPTURE 獲取位置,**倒序替換** 或用 preg_replace_callback 單次處理。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved