Czym jest parsowanie URL?
Parsowanie URL to proces rozbicia Jednolitego Lokalizatora Zasobów na jego poszczególne komponenty: protokół (schemat), nazwa hosta, port, ścieżka, parametry zapytania i identyfikator fragmentu. Każdy URL ma strukturę zdefiniowaną przez RFC 3986 i standard WHATWG URL. Parser URL odczytuje surowy ciąg, identyfikuje każdy segment po znakach ograniczników (://, :, /, ?, #, &, =) i zwraca je jako osobne, dostępne pola.
Przeglądarki parsują URL za każdym razem, gdy wpisujesz adres lub klikasz link. Konstruktor URL w JavaScript, moduł urllib.parse w Pythonie i pakiet net/url w Go implementują parsery zgodne z tymi samymi regułami strukturalnymi. Parsowanie URL jest odwrotnością kodowania URL: zamiast przekształcać znaki na potrzeby bezpiecznego przesyłania, rozkładasz już uformowany URL na części, z których się składa.
Typowy URL taki jak https://api.example.com:8080/v1/users?page=2&limit=10#section zawiera sześć odrębnych komponentów. Znaki ograniczników — ://, :, /, ?, &, = i # — sprawiają, że parsowanie jest deterministyczne: każdy z nich sygnalizuje granicę i pozwala parserowi wyodrębniać pola bez niejednoznaczności.
Dlaczego warto używać parsera URL online?
Ręczne dzielenie URL na oko jest podatne na błędy, szczególnie gdy ciąg zawiera zakodowane znaki, wiele parametrów zapytania lub niestandardowe porty. To narzędzie parsuje URL przy użyciu tego samego algorytmu zgodnego ze standardem WHATWG, którego używają przeglądarki, i wyświetla każdy komponent w przejrzystej, dającej się kopiować tabeli.
Przypadki użycia parsera URL
Dokumentacja komponentów URL
Poniższa tabela pokazuje każdą właściwość zwracaną przez konstruktor URL w JavaScript podczas parsowania URL. Te same komponenty istnieją w wynikach urlparse Pythona, strukturze url.URL Go i wyjściu parse_url PHP, choć nazwy właściwości różnią się w zależności od języka.
| Właściwość | Przykład | Opis |
|---|---|---|
| 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 |
Standard WHATWG URL vs RFC 3986
Dwie specyfikacje definiują sposób parsowania URL-i. Zgadzają się co do podstawowej struktury, ale różnią się w przypadkach brzegowych — i ta różnica jest zwykle przyczyną, gdy przeglądarka obsługuje URL inaczej niż serwer.
W praktyce większość różnic pojawia się przy parsowaniu URL-i z międzynarodowymi nazwami domen (IDN), brakującymi schematami lub nietypowymi znakami. Parser WHATWG automatycznie konwertuje nazwy hostów IDN na Punycode, podczas gdy ścisłe parsery RFC 3986 mogą je odrzucać. Jeśli wkleisz URL do tego narzędzia i zobaczysz inne wyniki niż te generowane przez kod po stronie serwera, różnica między WHATWG a RFC 3986 jest najczęstszą przyczyną.
Przykłady kodu
Każdy popularny język posiada wbudowany parser URL. Poniższe przykłady parsują ten sam URL i wyodrębniają jego komponenty. Zwróć uwagę na drobne różnice w nazewnictwie między językami: Python używa scheme zamiast protocol, a Go udostępnia RawQuery zamiast search.
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"