網頁採集時,頁面鏈接常為相對路徑(../、./、無前綴)、絕對根路徑(/path/),需轉換為絕對全路徑(https://domain/path/)才能正確請求。
<?php
/**
* 批量補全 HTML 中所有 href/src 鏈接為絕對 URL
* @param string $html 原始 HTML
* @param string $baseUrl 基礎 URL (如 https://www.jb51.net/)
* @return string 處理後 HTML
*/
function formatUrl(string $html, string $baseUrl): string {
// 正則匹配所有 和
(單雙引號)
if (preg_match_all(
'/(
]+src=["\']([^"\']+)["\'][^>]*>)|(]+href=["\']([^"\']+)["\'][^>]*>)/i',
$html, $matches, PREG_SET_ORDER
)) {
foreach ($matches as $m) {
$wholeTag = $m[0];
$url = $m[2] ?? $m[4]; // 提取 URL 部分
$newUrl = resolveUrl($url, $baseUrl);
$html = str_replace($wholeTag, str_replace($url, $newUrl, $wholeTag), $html);
}
}
return $html;
}
/**
* 單個 URL 解析補全
*/
function resolveUrl(string $url, string $baseUrl): string {
// 1. 已是絕對 URL 或特殊協議,跳過
if (preg_match('/^(https?|ftp):/i', $url)) return $url;
if (preg_match('/^(mailto:|javascript:|#)/i', $url)) return $url;
$parsed = parse_url($baseUrl);
$scheme = $parsed['scheme'] . '://';
$host = $parsed['host'];
$base = $scheme . $host;
$path = dirname($parsed['path'] ?? '/');
if ($path === '\\' || $path === '.') $path = '';
// 2. 絕對根路徑 /path/
if ($url[0] === '/') return $base . $url;
// 3. 相對路徑 ../ 或 ./
if (str_starts_with($url, '../')) {
while (str_starts_with($url, '../')) {
$url = substr($url, 3);
$path = dirname($path);
}
return $base . ($path ? "/$path" : '') . '/' . $url;
}
if (str_starts_with($url, './')) {
$url = substr($url, 2);
return $base . ($path ? "/$path" : '') . '/' . $url;
}
// 4. 無前綴相對路徑
return $base . ($path ? "/$path" : '') . '/' . $url;
}
// 使用示例
$base = 'https://www.jb51.net/';
$html = '<a href="/index.htm">首頁</a><a href="Resolvent/index.htm">方案</a>';
echo formatUrl($html, $base);
// 輸出: 首頁方案
| 原始 URL 類型 | 示例 | 補全結果 (基礎 https://a.com/dir/) |
|---|---|---|
| 絕對 URL | https://b.com/x.jpg | 保持不變 |
| 絕對根路徑 | /img/logo.png | https://a.com/img/logo.png |
| 相對父目錄 | ../style.css | https://a.com/style.css |
| 相對當前目錄 | ./script.js | https://a.com/dir/script.js |
| 無前綴 | page.html | https://a.com/dir/page.html |
| 錨點 | #section | 保持 (或補全當前頁 URL) |
| JS/郵件協議 | javascript:; / mailto:a@b.com | 保持不變 |
<base href=...> 或從響應 URL 推導。./、合併 //、處理 %20 等編碼。formatUrl。<?php
function formatUrlDom(string $html, string $baseUrl): string {
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML('' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
$xpath = new DOMXPath($dom);
foreach ($xpath->query('//a[@href] | //img[@src] | //link[@href] | //script[@src]') as $node) {
$attr = $node->hasAttribute('href') ? 'href' : 'src';
$url = $node->getAttribute($attr);
$node->setAttribute($attr, resolveUrl($url, $baseUrl));
}
return $dom->saveHTML();
}