采集第三方網站內容時,圖片常有防盜鏈限制,直接引用外鏈會顯示失效。需將圖片下載到自有服務器,再替換頁面中的圖片路徑。
src 屬性<?php
/** 確保目錄存在並可寫 */
function ensureDir(string $dir): void {
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
chmod($dir, 0755);
}
}
/** 讀取遠程/本地文件內容 */
function readFileContent(string $path): string|false {
$path = trim($path);
$fp = @fopen($path, 'rb');
if (!$fp) return false;
$content = '';
if (str_contains($path, '://')) { // 遠程流式讀取
while (!feof($fp)) {
$content .= fread($fp, 8192);
}
} else { // 本地一次性讀取
$content = fread($fp, filesize($path));
}
fclose($fp);
return $content;
}
/** 寫入文件 */
function writeFile(string $path, string $content): bool {
$fp = @fopen($path, 'wb');
if (!$fp) return false;
fwrite($fp, $content);
fclose($fp);
return true;
}
/** 從 URL 解析文件名(含擴展名) */
function parseFilename(string $url): string {
$parsed = parse_url($url);
$path = $parsed['path'] ?? '';
$name = basename($path);
// 去除查詢參數
if (($pos = strpos($name, '?')) !== false) {
$name = substr($name, 0, $pos);
}
// 無擴展名時默認 .jpg
if (!preg_match('/\.(jpg|jpeg|png|gif|webp)$/i', $name)) {
$name .= '.jpg';
}
return $name;
}
/**
* 下載遠程圖片到本地
* @param string $url 圖片完整 URL
* @param string $saveDir 本地存儲根目錄(絕對路徑)
* @return string|false 成功返回相對路徑,失敗返回 false
*/
function saveRemoteImage(string $url, string $saveDir = 'images/'): string|false {
$url = trim($url);
$url = str_replace(' ', '%20', $url); // 處理空格
$content = readFileContent($url);
if ($content === false || $content === '') {
error_log("Failed to fetch: $url");
return false;
}
$filename = parseFilename($url);
// 防衝突:時間戳 + 隨機數 + 原文件名
$uniqueName = time() . '_' . mt_rand(1000, 9999) . '_' . $filename;
// 按日期分目錄
$dateDir = date('Y/m/d');
$fullDir = rtrim($saveDir, '/') . '/' . $dateDir;
ensureDir($fullDir);
$fullPath = $fullDir . '/' . $uniqueName;
if (!writeFile($fullPath, $content)) {
error_log("Failed to write: $fullPath");
return false;
}
// 返回相對 Web 根目錄的路徑
return ltrim(str_replace($_SERVER['DOCUMENT_ROOT'], '', $fullPath), '/');
}
/**
* 采集頁面所有圖片並本地化
* @param string $html 頁面 HTML
* @param string $baseUrl 頁面基礎 URL(用於補全相對路徑)
* @param string $saveDir 存儲目錄
* @return string 處理後的 HTML(圖片 src 已替換為本地路徑)
*/
function localizeImages(string $html, string $baseUrl, string $saveDir = 'images/'): string {
$dom = new DOMDocument();
@$dom->loadHTML('' . $html); // 抑制警告
$xpath = new DOMXPath($dom);
$images = $xpath->query('//img[@src]');
foreach ($images as $img) {
$src = $img->getAttribute('src');
if (empty($src)) continue;
// 補全相對路徑
$absoluteUrl = parse_url($src, PHP_URL_SCHEME) ? $src : rtrim($baseUrl, '/') . '/' . ltrim($src, '/');
$localPath = saveRemoteImage($absoluteUrl, $saveDir);
if ($localPath) {
$img->setAttribute('src', $localPath);
// 可選:保留原始 src 作為 data-original
$img->setAttribute('data-original', $absoluteUrl);
}
}
// 輸出處理後的 body 內容
$body = $dom->getElementsByTagName('body')->item(0);
return $dom->saveHTML($body);
}
$targetUrl = 'https://example.com/news/123.html';
$html = file_get_contents($targetUrl);
// 1. 提取正文區域(需根據目標站點結構調整選擇器)
$dom = new DOMDocument();
@$dom->loadHTML('' . $html);
$xpath = new DOMXPath($dom);
$contentNode = $xpath->query('//div[@class="article-content"]')->item(0);
if (!$contentNode) exit('Content area not found');
$articleHtml = $dom->saveHTML($contentNode);
// 2. 本地化圖片
$processedHtml = localizeImages($articleHtml, $targetUrl, 'uploads/collected/');
// 3. 入庫或輸出
echo $processedHtml;
| 反采集手段 | 應對方案 |
|---|---|
| Referer 檢查 | curl 設置 CURLOPT_REFERER 為目標頁面 URL |
| User-Agent 封禁 | 使用真實瀏覽器 UA 池隨機切換 |
| 圖片 302 多次跳轉 | curl 開啟 CURLOPT_FOLLOWLOCATION 並限制最大跳轉次數 |
| Cookie/Session 驗證 | 先登錄獲取 Cookie,攜帶訪問 |
| 圖片動態簽名(帶時間戳參數) | 下載時忽略查詢參數,或模擬簽名算法 |
| IP 限流 | 代理 IP 池輪換,控制請求頻率 |
robots.txt 及服務條款。