PHPで相対パスとなっているURLを絶対パスに変換する方法

2021年08月18日2023年10月10日

WEBサイトをクローリングしていると絶対パスで記述されているサイトもあれば、相対パスで書かれているサイトも1ったりします。

そういったサイトのリンクをURL形式に変換するプログラムを作ってみました。

相対パスとなっているURLを絶対パスにしたい

サイトによってリンクの書き方は様々です。例えば下記のようなリンクがよくあります。

■サイトURL
http://example.com/

■サイト内のリンク

http://example.com/about/
/about/
../../about/
./dir1/dir2/

やりたいことは、こういった絶対パスで書かれているURLや相対パスで書かれているURLを絶対パスのURLにすべて統一したいということです。

関数を作って呼び出す

とりあえず関数化してみました。実際に使いながら改良していって変なプログラムになりましたが、問題なく動作しています。

<?php
// アクセスしているURL
$baseUrl = 'http://example.com/dir1/dir2';

//ページ内にあるURL
$linkList = ['../../about',
	'/contact',
	'./',
	'../',
	'../../files/test.pdf',
	'http://example.com/dir1/',
	'http://日本語ドメインの外部サイト.com/',
];
foreach ($linkList as $key => $link){
	$url = getNormalizationUrl($baseUrl, $link);
	var_dump("【{$key}】Q:{$link}");
	var_dump("【{$key}】A:{$url}");
}

function getNormalizationUrl($baseUrl, $link, $isPunycode = TRUE)
{
	$baseParse = parse_url($baseUrl);
	
	$linkParse = parse_url($link);
	
	// asciiに変換
	if ($isPunycode) {
		if (isset($baseParse['host'])) {
			$baseParse['host'] = idn_to_ascii($baseParse['host']);
		}
		
		if (isset($linkParse['host'])) {
			$linkParse['host'] = idn_to_ascii($linkParse['host']);
		}
	}
	
	$url = null;
	if (isset($linkParse['host'])) {
		$url = "{$linkParse['scheme']}://{$linkParse['host']}{$linkParse['path']}" .
			 (isset($linkParse['query']) ? '?' . $linkParse['query'] : '');
		;
	} elseif (isset($linkParse['path'])) {
		
		if ((in_array(substr($linkParse['path'], 0, 2), ['./','..']))) {
			
			$slashList = explode('/', $linkParse['path']);
			
			$basePath = isset($baseParse['path']) ? $baseParse['path'] : '';
			// 拡張子が存在すればファイル名と判断し削除する
			if (pathinfo($basePath, PATHINFO_EXTENSION)) {
				// ファイル名を除く
				$basePath = pathinfo($basePath, PATHINFO_DIRNAME);
			}
			
			foreach ($slashList as $val) {
				
				if ($val == '..') {
					$basePath = dirname($basePath);
				}
				if (in_array($basePath, ['/','\\'])) {
					$basePath = '';
					break;
				}
			}
			
			$url = $baseParse['scheme'] . '://' . $baseParse['host'] . $basePath . '/' . str_replace(['../','./'], 
				'', $link);
		} elseif (substr($linkParse['path'], 0, 1) == '/') {
			$url = $baseParse['scheme'] . '://' . $baseParse['host'] . $link;
		} else {
			$basePath = isset($baseParse['path']) ? $baseParse['path'] : '';
			
			if (pathinfo($basePath, PATHINFO_EXTENSION)) {
				$basePath = pathinfo($basePath, PATHINFO_DIRNAME);
			}
			
			$url = $baseParse['scheme'] . '://' . str_replace("//", "/", $baseParse['host'] . '/' . $basePath . '/' .
				 $link);
		}
	}
	return $url;
}

?>

 

実行結果は下記になります。

string(22) "【0】Q:../../about"
string(35) "【0】A:http://example.com/about"
string(19) "【1】Q:/contact"
string(37) "【1】A:http://example.com/contact"
string(13) "【2】Q:./"
string(40) "【2】A:http://example.com/dir1/dir2/"
string(14) "【3】Q:../"
string(35) "【3】A:http://example.com/dir1/"
string(31) "【4】Q:../../files/test.pdf"
string(44) "【4】A:http://example.com/files/test.pdf"
string(35) "【5】Q:http://example.com/dir1/"
string(35) "【5】A:http://example.com/dir1/"
string(62) "【6】Q:http://日本語ドメインの外部サイト.com/"
string(62) "【6】A:http://xn--u9jwfb1eyeb2ouf2669ay00a16cdy1mgdra.com/"

相対パスでのリンクに関しては現在のアクセスしているURLを下にURLを生成して返却します。

URLでのリンクの場合は、処理をせずそのまま返却しています。

日本語ドメインなどのURLに関しては、英字の文字列に変換して返却しています。

まとめ

現時点でこの関数でURLを正規化して1日50万ページを解析しています。今のところエラーもなく動作しているので大丈夫と思われます。

といいつつ、使用する際は自己責任のもと使用してください。

 

コメント

コメントを残す

お名前(任意)
コメント:新規