需求:輸入網頁 URL,自動解析所有 <img> 標籤的 src,處理相對/絕對路徑,去重下載至本地目錄。
src 屬性值base_url 或網域)array_unique)getimagesize() 偵測 MIME/類型imagecreatefromjpeg 等)imagejpeg/png/gif/... 儲存,可指定品質<?php
class ImageDownloader {
private $saveDir;
private $baseUrl;
private $domain;
public function __construct($pageUrl, $saveDir = './images/') {
$this->saveDir = rtrim($saveDir, '/') . '/';
if (!is_dir($this->saveDir)) mkdir($this->saveDir, 0755, true);
$parsed = parse_url($pageUrl);
$this->domain = $parsed['host'] ?? '';
$this->baseUrl = substr($pageUrl, 0, strrpos($pageUrl, '/') + 1);
}
public function downloadAll() {
$ctx = stream_context_create(['http'=>['timeout'=>10,'request_fulluri'=>true]]);
$html = @file_get_contents($this->baseUrl, false, $ctx);
if (!$html) return ['error'=>'無法取得網頁內容'];
preg_match_all('/<img[^>]+src=["\']([^"\']+)["\']/i', $html, $m);
$urls = array_unique($m[1] ?? []);
$results = [];
foreach ($urls as $url) {
$absUrl = $this->toAbsolute($url);
$results[] = $this->downloadOne($absUrl);
}
return $results;
}
private function toAbsolute($url) {
if (stripos($url, 'http') === 0) return $url;
if ($url[0] === '/') return "https://{$this->domain}{$url}";
return $this->baseUrl . $url;
}
private function downloadOne($url) {
$info = @getimagesize($url);
if (!$info) return ['url'=>$url, 'status'=>'invalid_image'];
$mime = $info['mime'];
$extMap = [
'image/jpeg' => ['func'=>'imagecreatefromjpeg', 'save'=>'imagejpeg', 'ext'=>'jpg', 'quality'=>90],
'image/png' => ['func'=>'imagecreatefrompng', 'save'=>'imagepng', 'ext'=>'png'],
'image/gif' => ['func'=>'imagecreatefromgif', 'save'=>'imagegif', 'ext'=>'gif'],
'image/webp' => ['func'=>'imagecreatefromwebp', 'save'=>'imagewebp', 'ext'=>'webp'],
];
$cfg = $extMap[$mime] ?? $extMap['image/jpeg'];
$img = @$cfg['func']($url);
if (!$img) return ['url'=>$url, 'status'=>'gd_load_failed'];
$filename = basename(parse_url($url, PHP_URL_PATH));
$filename = pathinfo($filename, PATHINFO_FILENAME) . '.' . $cfg['ext'];
$filepath = $this->saveDir . $filename;
$saveFunc = $cfg['save'];
$quality = $cfg['quality'] ?? null;
$ok = $quality ? $saveFunc($img, $filepath, $quality) : $saveFunc($img, $filepath);
imagedestroy($img);
return ['url'=>$url, 'path'=>$filepath, 'status'=>$ok ? 'success' : 'save_failed'];
}
}
// 使用範例
$downloader = new ImageDownloader('https://example.com/article', './downloads/');
$results = $downloader->downloadAll();
print_r($results);
| 環節 | 技術 | 說明 |
|---|---|---|
| HTML 取得 | stream_context_create | 統一控制 timeout、proxy、User-Agent |
| 正則提取 | /<img[^>]+src=["\']([^"\']+)["\']/i | 支援單/雙引號、屬性順序不固定 |
| 相對路徑轉換 | parse_url + 判斷 | 處理 /path、path、https://... 三種 |
| 類型偵測 | getimagesize() | 回傳 mime、寬高,比單看副檔名可靠 |
| GD 載入/儲存 | imagecreatefrom* / image* | 統一轉檔、可調品質、支援主流格式 |
'header'=>"Referer: {$pageUrl}\r\n"getimagesize 判斷尺寸,過大跳過或用 Imagick 串流處理。curl_multi、Guzzle Promise 或 Swoole 協程並發。