Python9 мин чтения

Как выполнить URL-кодирование в Python (полное руководство по urllib.parse)

URL-кодирование в Python выполняется с помощью urllib.parse.quote() для percent-кодирования строк и urllib.parse.urlencode() для кодирования словарей в строки запроса. В этом руководстве разбираются quote(), unquote(), urlencode() и parse_qs() на практических примерах.

URL-кодирование с помощью quote()

Функция urllib.parse.quote() — это основной инструмент Python для percent-кодирования строк. Она преобразует символы, небезопасные для использования в URL, в их percent-кодированные эквиваленты. По умолчанию прямые слэши (/) считаются безопасными символами, но это поведение можно настроить.

from urllib.parse import quote

# Базовое кодирование
print(quote('hello world'))
# Вывод: hello%20world

# Кодирование специальных символов
print(quote('price=10&qty=2'))
# Вывод: price%3D10%26qty%3D2

# По умолчанию / не кодируется
print(quote('path/to/file'))
# Вывод: path/to/file

# Чтобы закодировать и слэши, задайте safe=''
print(quote('path/to/file', safe=''))
# Вывод: path%2Fto%2Ffile

# Кодирование Unicode-символов
print(quote('cafe'))
# Вывод: caf%C3%A9

# Указание дополнительных безопасных символов
print(quote('key=value&foo=bar', safe='=&'))
# Вывод: key=value&foo=bar

Параметр safe — это ключ к управлению тем, что именно кодируется. По умолчанию safe='/'. Если нужно закодировать всё, кроме буквенно-цифровых символов и _.-~, задайте safe=''. Это эквивалентно функции encodeURIComponent() в JavaScript.

Существует также функция quote_plus(), которая работает как quote(), но кодирует пробелы как + вместо %20. Именно этот формат используется в данных HTML-форм (application/x-www-form-urlencoded).

from urllib.parse import quote_plus

print(quote_plus('hello world'))
# Вывод: hello+world

print(quote_plus('key=value&name=John Doe'))
# Вывод: key%3Dvalue%26name%3DJohn+Doe

URL-декодирование с помощью unquote()

Функция urllib.parse.unquote() выполняет обратное percent-кодирование, преобразуя последовательности %XX обратно в исходные символы. Существует также функция unquote_plus(), которая дополнительно преобразует знаки + в пробелы.

from urllib.parse import unquote, unquote_plus

# Базовое декодирование
print(unquote('hello%20world'))
# Вывод: hello world

print(unquote('caf%C3%A9'))
# Вывод: cafe (с диакритическим знаком)

# unquote НЕ преобразует + в пробел
print(unquote('hello+world'))
# Вывод: hello+world

# unquote_plus преобразует + в пробел
print(unquote_plus('hello+world'))
# Вывод: hello world

# Декодирование полного URL
url = 'https://example.com/search?q=C%2B%2B%20programming'
print(unquote(url))
# Вывод: https://example.com/search?q=C++ programming

Всегда используйте unquote_plus() при декодировании данных форм, поскольку HTML-формы кодируют пробелы как +. Используйте unquote() для общего URL-декодирования, где пробелы закодированы как %20.

Кодирование строк запроса с помощью urlencode()

Функция urllib.parse.urlencode() принимает словарь или список кортежей и преобразует их в правильно отформатированную строку запроса. Это наиболее удобный способ построения строк запроса в Python.

from urllib.parse import urlencode

# Словарь в строку запроса
params = {
    'q': 'python programming',
    'page': 1,
    'lang': 'en'
}
print(urlencode(params))
# Вывод: q=python+programming&page=1&lang=en

# Список кортежей (сохраняет порядок, допускает повторяющиеся ключи)
params = [
    ('tag', 'python'),
    ('tag', 'web'),
    ('sort', 'date')
]
print(urlencode(params))
# Вывод: tag=python&tag=web&sort=date

# Использование doseq=True для значений-списков
params = {
    'tag': ['python', 'web', 'api'],
    'sort': 'date'
}
print(urlencode(params, doseq=True))
# Вывод: tag=python&tag=web&tag=api&sort=date

# Использование quote_via для управления кодированием пробелов
from urllib.parse import quote
params = {'q': 'hello world'}
print(urlencode(params, quote_via=quote))
# Вывод: q=hello%20world  (использует %20 вместо +)

По умолчанию urlencode() внутренне использует quote_plus(), а значит пробелы становятся +. Если для пробелов нужен %20, передайте quote_via=quote, как показано выше.

Разбор строк запроса с помощью parse_qs()

Функция urllib.parse.parse_qs() разбирает строку запроса обратно в словарь. Каждое значение в словаре представляет собой список, поскольку параметры запроса могут иметь несколько значений. Существует также функция parse_qsl(), которая возвращает список кортежей.

from urllib.parse import parse_qs, parse_qsl

# Разбор строки запроса в словарь
qs = 'q=python+programming&page=1&lang=en'
result = parse_qs(qs)
print(result)
# Вывод: {'q': ['python programming'], 'page': ['1'], 'lang': ['en']}

# Примечание: значения всегда являются списками
print(result['q'][0])  # 'python programming'

# Обработка нескольких значений для одного ключа
qs = 'tag=python&tag=web&tag=api'
result = parse_qs(qs)
print(result)
# Вывод: {'tag': ['python', 'web', 'api']}

# parse_qsl возвращает список кортежей
result = parse_qsl(qs)
print(result)
# Вывод: [('tag', 'python'), ('tag', 'web'), ('tag', 'api')]

# Сохранение пустых значений (по умолчанию они опускаются)
qs = 'name=John&email=&age=30'
print(parse_qs(qs, keep_blank_values=True))
# Вывод: {'name': ['John'], 'email': [''], 'age': ['30']}

Кодирование полных URL с помощью urlparse

При работе с полными URL функции Python urlparse() и urlunparse() позволяют безопасно разбирать и заново собирать URL. Это особенно полезно, когда нужно изменить отдельные части URL, не нарушая его структуру.

from urllib.parse import urlparse, urlunparse, urlencode, quote

# Разбор URL на компоненты
url = 'https://example.com/search?q=hello&page=1#results'
parsed = urlparse(url)
print(parsed.scheme)    # 'https'
print(parsed.netloc)    # 'example.com'
print(parsed.path)      # '/search'
print(parsed.query)     # 'q=hello&page=1'
print(parsed.fragment)  # 'results'

# Построение URL из компонентов
from urllib.parse import ParseResult
new_url = urlunparse(ParseResult(
    scheme='https',
    netloc='api.example.com',
    path='/v2/search',
    params='',
    query=urlencode({'q': 'python & java', 'limit': 10}),
    fragment=''
))
print(new_url)
# Вывод: https://api.example.com/v2/search?q=python+%26+java&limit=10

# Безопасное добавление сегмента пути со специальными символами
base = 'https://example.com/files/'
filename = 'my report (final).pdf'
safe_url = base + quote(filename, safe='')
print(safe_url)
# Вывод: https://example.com/files/my%20report%20%28final%29.pdf

Для современного кода на Python стоит рассмотреть библиотеку requests, которая автоматически выполняет URL-кодирование, когда вы передаёте параметры в виде словаря. Библиотека httpx также предоставляет аналогичные возможности автоматического кодирования.

import requests

# requests выполняет кодирование автоматически
response = requests.get(
    'https://api.example.com/search',
    params={
        'q': 'python & java',
        'page': 1,
        'sort': 'relevance'
    }
)
print(response.url)
# https://api.example.com/search?q=python+%26+java&page=1&sort=relevance

Похожие статьи

Попробуйте наши бесплатные инструменты