← Назад к вопросам

Из чего состоит URL

1.0 Junior🔥 161 комментариев
#REST API и HTTP

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Структура URL (Uniform Resource Locator)

URL — это стандартизированный способ адресации ресурсов в интернете. Каждый URL состоит из нескольких компонентов, каждый из которых имеет своё назначение.

Полная структура URL

https://user:password@example.com:8443/path/to/resource?key=value&foo=bar#section
├────┬────────────────────────────┬──────┬──────────────────┬─────────────────┬────────┤
│    │                            │      │                  │                 │        │
Scheme Authority (Host Info)      Port   Path               Query String    Fragment

1. Scheme (протокол)

https://example.com/path
^^^^^^
scheme

Определяет, как обращаться к ресурсу.

Популярные схемы:

  • http:// — HTTP, незащищённый, порт 80
  • https:// — HTTP с TLS, защищённый, порт 443
  • ftp:// — File Transfer Protocol, порт 21
  • ws:// — WebSocket, незащищённый, порт 80
  • wss:// — WebSocket Secure, защищённый, порт 443
  • file:// — локальный файл
  • mailto: — email адрес
  • tel: — телефонный номер
from urllib.parse import urlparse

url = "https://example.com/path"
parsed = urlparse(url)
print(parsed.scheme)  # 'https'

2. Authority (информация об авторизации)

https://user:password@example.com:8443/path
       ──────────────────────────────
       authority

Авторити состоит из трёх частей:

a) Userinfo (опционально)

https://user:password@example.com/path
       ─────────────
       userinfo
  • user — имя пользователя
  • password — пароль
  • Редко используется в современных приложениях (security risk)
url = "https://admin:secret123@internal.example.com/api"
parsed = urlparse(url)
print(parsed.username)   # 'admin'
print(parsed.password)   # 'secret123'

# ⚠️ Не передавайте пароли в URL! Используйте Headers или Environment variables

b) Host (домен)

https://example.com:8443/path
       ───────────
       host

Форматы:

  • Доменное имя: example.com, api.example.com, sub.domain.example.co.uk
  • IPv4: 192.168.1.1
  • IPv6: [2001:db8::1] (в квадратных скобках)
  • localhost: localhost, 127.0.0.1
url = "http://192.168.1.100:8000/api"
parsed = urlparse(url)
print(parsed.hostname)  # '192.168.1.100'

url_ipv6 = "http://[2001:db8::1]:8000/api"
parsed = urlparse(url_ipv6)
print(parsed.hostname)  # '2001:db8::1'

c) Port (порт)

https://example.com:8443/path
                    ────
                    port

Стандартные порты (опционально указываются):

  • HTTP: 80 (если опустить, подразумевается)
  • HTTPS: 443 (если опустить, подразумевается)
  • FTP: 21
  • SSH: 22
  • PostgreSQL: 5432
  • MySQL: 3306
  • MongoDB: 27017
  • Redis: 6379
url = "https://example.com/path"  # port не указан
parsed = urlparse(url)
print(parsed.port)  # None (подразумевается 443 для HTTPS)

url = "https://example.com:8443/path"
parsed = urlparse(url)
print(parsed.port)  # 8443
print(parsed.hostname)  # 'example.com'

3. Path (путь до ресурса)

https://example.com/api/v1/users/123?query=1
                    ──────────────────
                    path

Характеристики:

  • Начинается с /
  • Разделяется слешами
  • Регистрочувствителен (обычно)
  • Может быть пустой (подразумевается /)
url = "https://example.com/api/users/123"
parsed = urlparse(url)
print(parsed.path)  # '/api/users/123'

# Разбор пути
path_parts = parsed.path.strip('/').split('/')
print(path_parts)  # ['api', 'users', '123']

# Кодирование спецсимволов в пути
from urllib.parse import quote

user_input = "hello world"
encoded = quote(user_input)
print(f"Path: /search/{encoded}")  # /search/hello%20world

4. Query String (параметры запроса)

https://example.com/path?key1=value1&key2=value2&key3=value3
                      ────────────────────────────────────────
                      query string

Характеристики:

  • Начинается с ?
  • Пары ключ=значение, разделённые &
  • Порядок не гарантирован
  • Значения кодируются (URL encoding)
  • Может быть пустой
from urllib.parse import urlparse, parse_qs, urlencode

url = "https://example.com/search?q=python&limit=10&offset=0"
parsed = urlparse(url)

# Получить строку запроса
print(parsed.query)  # 'q=python&limit=10&offset=0'

# Распарсить в словарь
query_params = parse_qs(parsed.query)
print(query_params)  # {'q': ['python'], 'limit': ['10'], 'offset': ['0']}

# Создать URL с параметрами
params = {'q': 'python', 'limit': 10, 'offset': 0}
query_string = urlencode(params)
print(f"https://example.com/search?{query_string}")
# https://example.com/search?q=python&limit=10&offset=0

# Спецсимволы кодируются
params = {'search': 'hello world', 'category': 'python tips'}
query_string = urlencode(params)
print(f"https://example.com/search?{query_string}")
# https://example.com/search?search=hello+world&category=python+tips

5. Fragment (якорь)

https://example.com/docs/api?version=2#authentication
                                        ──────────────
                                        fragment

Характеристики:

  • Начинается с #
  • Используется для навигации внутри страницы
  • Не отправляется на сервер (остаётся на клиенте)
  • Обычно используется в SPA приложениях для маршрутизации
url = "https://example.com/page#section-2"
parsed = urlparse(url)
print(parsed.fragment)  # 'section-2'

# Пример: SPA маршрутизация
url = "https://myapp.com/app#/users/123/profile"
parsed = urlparse(url)
print(parsed.fragment)  # '/users/123/profile'
# На клиенте JavaScript прочитает этот фрагмент и отобразит нужный компонент

Примеры реальных URL

from urllib.parse import urlparse

# Пример 1: простой API запрос
url1 = "https://api.github.com/repos/pallets/flask"
parsed1 = urlparse(url1)
print(f"Host: {parsed1.hostname}")  # api.github.com
print(f"Path: {parsed1.path}")      # /repos/pallets/flask

# Пример 2: поисковый запрос
url2 = "https://www.google.com/search?q=python+programming&hl=en"
parsed2 = urlparse(url2)
print(f"Host: {parsed2.hostname}")  # www.google.com
print(f"Path: {parsed2.path}")      # /search
print(f"Query: {parsed2.query}")    # q=python+programming&hl=en

# Пример 3: документация с якорем
url3 = "https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse"
parsed3 = urlparse(url3)
print(f"Path: {parsed3.path}")      # /3/library/urllib.parse.html
print(f"Fragment: {parsed3.fragment}")  # urllib.parse.urlparse

# Пример 4: localhost с портом
url4 = "http://localhost:8000/api/v1/users?limit=10"
parsed4 = urlparse(url4)
print(f"Hostname: {parsed4.hostname}")  # localhost
print(f"Port: {parsed4.port}")          # 8000
print(f"Path: {parsed4.path}")          # /api/v1/users

URL Encoding (% encoding)

Спецсимволы кодируются в URL:

from urllib.parse import quote, unquote

# Кодирование
text = "hello world!"
encoded = quote(text)  # safe characters: /_.-~
print(encoded)  # hello%20world%21

# С определённым набором безопасных символов
path = "/api/v1/users/john@example.com"
encoded = quote(path, safe="/")  # Сохранить слеши
print(encoded)  # /api/v1/users/john%40example.com

# Декодирование
decoded = unquote("hello%20world%21")
print(decoded)  # hello world!

# Таблица кодирования
print("%20 = space")
print("%2F = /")
print("%3F = ?")
print("%23 = #")
print("%40 = @")
print("%3D = =")
print("%26 = &")

Построение URL

from urllib.parse import urljoin, urlunparse

# Способ 1: urljoin (относительные URL)
base = "https://example.com/api/v1/"
relative = "users/123"
full_url = urljoin(base, relative)
print(full_url)  # https://example.com/api/v1/users/123

# Способ 2: urlunparse (все части вместе)
components = (
    'https',  # scheme
    'example.com',  # netloc
    '/api/users',  # path
    '',  # params
    'limit=10&offset=0',  # query
    'results'  # fragment
)
url = urlunparse(components)
print(url)  # https://example.com/api/users?limit=10&offset=0#results

Валидация URL

from urllib.parse import urlparse
import re

def is_valid_url(url):
    # Базовая проверка
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except Exception:
        return False

print(is_valid_url("https://example.com"))  # True
print(is_valid_url("not a url"))  # False
print(is_valid_url("//example.com"))  # False

# Более строгая регулярка
url_pattern = re.compile(
    r'^https?://'  # схема
    r'(?:[a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+'  # домен
    r'(?:\.[a-zA-Z]{2,})'  # TLD
    r'(?::[0-9]+)?'  # опциональный порт
    r'(?:/[a-zA-Z0-9._~:/?#@!$&\'()*+,;=-]*)?$'  # опциональный путь
)

print(url_pattern.match("https://example.com"))  # Match
print(url_pattern.match("https://sub.example.co.uk:8443/path"))  # Match

Best Practices

# ✅ Используйте urllib.parse для работы с URL
from urllib.parse import urlparse, parse_qs, urlencode, quote

# ✅ Всегда кодируйте пользовательский ввод в URL
user_input = request.args.get('search')
encoded = quote(user_input)
url = f"https://api.example.com/search?q={encoded}"

# ✅ Валидируйте URL перед использованием
if is_valid_url(url):
    response = requests.get(url)

# ❌ Не конкатенируйте URL вручную
bad = f"https://example.com/search?q={user_input}"  # vulnerable

# ❌ Не передавайте credentials в URL
bad = f"https://user:password@example.com/api"  # security risk

# ✅ Используйте заголовки для авторизации
headers = {'Authorization': 'Bearer token123'}
response = requests.get(url, headers=headers)

Вывод

URL состоит из:

  1. Scheme — протокол (http, https, ftp)
  2. Authority — host и опциональные userinfo и port
  3. Path — путь к ресурсу
  4. Query — параметры запроса
  5. Fragment — якорь на странице

Понимание структуры URL — это essential skill для веб-разработчика. Правильная работа с URL гарантирует безопасность, читаемость и совместимость кода.