URL 解析とは?
URL 解析(パース)とは、Uniform Resource Locator を個々の構成要素——プロトコル(スキーム)、ホスト名、ポート、パス名、クエリパラメータ、フラグメント識別子——に分解するプロセスです。あらゆる URL は RFC 3986 および WHATWG URL Standard で定義された構造に従います。URL パーサーは生の文字列を読み取り、区切り文字(://、:、/、?、#、&、=)によって各セグメントを識別し、個別にアクセス可能なフィールドとして返します。
ブラウザはアドレスを入力するたび、またはリンクをクリックするたびに URL 解析を実行します。JavaScript の URL コンストラクタ、Python の urllib.parse モジュール、Go の net/url パッケージはいずれも同じ構造ルールに従ったパーサーを実装しています。URL 解析は URL エンコードの逆の操作です——安全な転送のために文字を変換するのではなく、すでに構成された URL をその構成部品に分解します。
https://api.example.com:8080/v1/users?page=2&limit=10#section のような典型的な URL には6つの異なる構成要素が含まれています。区切り文字——://、:、/、?、&、=、#——があるため解析が決定的になります。それぞれがセクションの境界を示し、パーサーは曖昧さなくフィールドを抽出できます。
このオンライン URL パーサーを使う理由
目視で URL を分割するのはミスが生じやすく、特に文字列にエンコードされた文字、複数のクエリパラメータ、非標準ポートが含まれる場合は困難です。このツールはブラウザが使用するのと同じ WHATWG 準拠のアルゴリズムで URL を解析し、すべての構成要素を見やすくコピー可能なテーブルで表示します。
URL パーサーのユースケース
URL コンポーネントリファレンス
以下のテーブルは、JavaScript の URL コンストラクタが URL を解析する際に返すすべてのプロパティを示しています。Python の urlparse 結果、Go の url.URL 構造体、PHP の parse_url の出力にも同じコンポーネントが存在しますが、言語によってプロパティ名は異なります。
| プロパティ | 例 | 説明 |
|---|---|---|
| protocol | https: | Scheme including the trailing colon |
| hostname | api.example.com | Domain name or IP address |
| port | 8080 | Port number (empty string if default) |
| pathname | /v1/users | Path starting with / |
| search | ?page=2&limit=10 | Query string including the leading ? |
| hash | #section | Fragment identifier including the leading # |
| origin | https://api.example.com:8080 | protocol + hostname + port |
| host | api.example.com:8080 | hostname + port |
| username | admin | Credentials before @ (rarely used in practice) |
| password | secret | Credentials before @ (avoid in production URLs) |
| href | (full URL) | The complete, serialized URL string |
WHATWG URL Standard と RFC 3986
URL の解析方法を定義する2つの仕様があります。基本構造については一致していますが、エッジケースで異なる動作をします——その差異が、ブラウザとサーバーで URL の扱いが異なる原因となることがほとんどです。
実際には、ほとんどの差異は国際ドメイン名(IDN)、スキームの欠落、特殊文字を含む URL を解析するときに現れます。WHATWG パーサーは IDN ホスト名を自動的に Punycode に変換しますが、厳格な RFC 3986 パーサーはそれを拒否することがあります。このツールに URL を貼り付けてサーバーサイドコードの結果と異なる場合、WHATWG と RFC の差異が最も可能性の高い原因です。
コード例
主要な言語にはすべて組み込みの URL パーサーがあります。以下の例では同じ URL を解析してその構成要素を抽出しています。言語間でのわずかな命名の違いに注目してください:Python では protocol の代わりに scheme を使用し、Go では search の代わりに RawQuery を公開します。
const url = new URL('https://api.example.com:8080/v1/users?page=2&limit=10#section')
url.protocol // → "https:"
url.hostname // → "api.example.com"
url.port // → "8080"
url.pathname // → "/v1/users"
url.search // → "?page=2&limit=10"
url.hash // → "#section"
// Iterate over query parameters
for (const [key, value] of url.searchParams) {
console.log(`${key} = ${value}`)
}
// → "page = 2"
// → "limit = 10"
// Modify and re-serialize
url.searchParams.set('page', '3')
url.toString()
// → "https://api.example.com:8080/v1/users?page=3&limit=10#section"from urllib.parse import urlparse, parse_qs
result = urlparse('https://api.example.com:8080/v1/users?page=2&limit=10#section')
result.scheme # → 'https'
result.hostname # → 'api.example.com'
result.port # → 8080
result.path # → '/v1/users'
result.query # → 'page=2&limit=10'
result.fragment # → 'section'
# Parse query string into a dict
params = parse_qs(result.query)
params['page'] # → ['2']
params['limit'] # → ['10']
# Reconstruct with modifications
from urllib.parse import urlencode, urlunparse
new_query = urlencode({'page': '3', 'limit': '10'})
urlunparse(result._replace(query=new_query))
# → 'https://api.example.com:8080/v1/users?page=3&limit=10#section'package main
import (
"fmt"
"net/url"
)
func main() {
u, err := url.Parse("https://api.example.com:8080/v1/users?page=2&limit=10#section")
if err != nil {
panic(err)
}
fmt.Println(u.Scheme) // → "https"
fmt.Println(u.Hostname()) // → "api.example.com"
fmt.Println(u.Port()) // → "8080"
fmt.Println(u.Path) // → "/v1/users"
fmt.Println(u.RawQuery) // → "page=2&limit=10"
fmt.Println(u.Fragment) // → "section"
// Query params as map
q := u.Query()
fmt.Println(q.Get("page")) // → "2"
fmt.Println(q.Get("limit")) // → "10"
}<?php $url = 'https://api.example.com:8080/v1/users?page=2&limit=10#section'; $parts = parse_url($url); $parts['scheme']; // → "https" $parts['host']; // → "api.example.com" $parts['port']; // → 8080 $parts['path']; // → "/v1/users" $parts['query']; // → "page=2&limit=10" $parts['fragment']; // → "section" // Parse query string into an array parse_str($parts['query'], $params); $params['page']; // → "2" $params['limit']; // → "10"