Что такое разбор URL?
Разбор URL — это процесс разделения Uniform Resource Locator на отдельные компоненты: протокол (схема), имя хоста, порт, путь, параметры запроса и идентификатор фрагмента. Каждый URL следует структуре, определённой в RFC 3986 и стандарте WHATWG URL. Парсер URL читает исходную строку, определяет каждый сегмент по символам-разделителям (://, :, /, ?, #, &, =) и возвращает их как отдельные, доступные поля.
Браузеры выполняют разбор URL каждый раз, когда вы вводите адрес или переходите по ссылке. Конструктор URL в JavaScript, модуль urllib.parse в Python и пакет net/url в Go реализуют парсеры, следующие одним и тем же структурным правилам. Разбор URL — операция, обратная кодированию URL: вместо преобразования символов для безопасной передачи вы разбиваете уже сформированный URL на составляющие его части.
Типичный URL вида https://api.example.com:8080/v1/users?page=2&limit=10#section содержит шесть отдельных компонентов. Символы-разделители — ://, :, /, ?, &, =, и # — делают разбор детерминированным: каждый из них обозначает границу и позволяет парсеру извлекать поля без неоднозначности.
Зачем использовать онлайн-парсер URL?
Вручную разбивать URL на части ненадёжно, особенно если строка содержит закодированные символы, несколько параметров запроса или нестандартные порты. Этот инструмент разбирает URL с помощью того же алгоритма, совместимого со стандартом WHATWG, который используют браузеры, и отображает каждый компонент в понятной таблице с возможностью копирования.
Сценарии использования парсера URL
Справочник по компонентам URL
В таблице ниже показаны все свойства, возвращаемые конструктором JavaScript URL при разборе URL. Те же компоненты присутствуют в результате urlparse в Python, структуре url.URL в Go и выводе parse_url в PHP, хотя имена свойств различаются в зависимости от языка.
| Свойство | Пример | Описание |
|---|---|---|
| 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 против RFC 3986
Два стандарта определяют правила разбора URL. Они согласованы в отношении базовой структуры, но расходятся в граничных случаях — и именно это расхождение чаще всего является причиной того, что браузер обрабатывает URL иначе, чем сервер.
На практике большинство различий проявляется при разборе URL с международными доменными именами (IDN), отсутствующими схемами или нестандартными символами. Парсер WHATWG автоматически преобразует IDN-хосты в Punycode, тогда как строгие парсеры RFC 3986 могут их отклонять. Если вы вставляете URL в этот инструмент и видите результат, отличный от выдаваемого вашим серверным кодом, наиболее вероятная причина — расхождение между WHATWG и RFC 3986.
Примеры кода
В каждом популярном языке есть встроенный парсер URL. Примеры ниже разбирают один и тот же URL и извлекают его компоненты. Обратите внимание на незначительные различия в именах: Python использует scheme вместо protocol, а Go предоставляет RawQuery вместо 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"