Параметры HTTP запроса в Python: от базовых до продвинутых техник

Раздел: Data Engineering -> Веб-программирование

Основные способы передачи параметров HTTP запроса

Параметры HTTP запроса (query string parameters) используются для передачи данных на сервер через URL. В Python существует несколько подходов для их формирования и отправки. Наиболее удобным и распространённым является использование библиотеки requests, которая автоматически кодирует параметры и обрабатывает специальные символы.

Библиотека requests: параметр params

Этот способ считается наиболее эффективным для синхронных запросов. Параметры передаются в виде словаря, и библиотека сама формирует корректную строку запроса, кодируя пробелы, кириллицу и другие символы.

import requests

params = {
    'search': 'Python HTTP',
    'page': 1,
    'limit': 10
}
response = requests.get('https://api.example.com/items', params=params)
print(response.url)

параметры запроса python (параметры http запроса в python)

https://api.example.com/items?search=Python+HTTP&page=1&limit=10

Пояснение: Ключи и значения автоматически экранируются (пробел заменяется на +). Можно передавать списки значений, используя список в качестве значения:

params = {'tag': ['python', 'http']}
response = requests.get('https://api.example.com/items', params=params)
print(response.url)
https://api.example.com/items?tag=python&tag=http

Типичная ошибка: Если передать в параметрах значения None, requests не включит их в URL. Иногда это неожиданно, если нужно явно передать пустой параметр. Решение - использовать пустую строку вместо None.

Проблема с неверным типом: если ключи или значения не строки, requests преобразует их через str(). Для булевых значений стоит явно приводить к строке, если требуется 'true'/'false'.

Как передать параметры без сторонних библиотек?

Используется встроенная библиотека urllib и функция urlencode из модуля urllib.parse. Этот вариант подходит, когда нельзя устанавливать внешние пакеты.

from urllib.parse import urlencode
from urllib.request import urlopen

params = {'q': 'python requests', 'page': 2}
query_string = urlencode(params)
url = 'https://api.example.com/search?' + query_string
with urlopen(url) as response:
    print(response.read().decode())

Пояснение: urlencode кодирует словарь в строку запроса. Результат можно объединить с базовым URL. Для добавления нескольких одинаковых ключей (например, tag=python&tag=http) следует передать список кортежей:

params = [('tag', 'python'), ('tag', 'http')]
query_string = urlencode(params)
print(query_string)
tag=python&tag=http

Проблема: urlencode по умолчанию кодирует пробел как '+', что соответствует application/x-www-form-urlencoded, но не всем серверам это подходит. Для использования %20 требуется параметр quote_via. Также необходимо самостоятельно обрабатывать исключения при сетевых ошибках.

Как работать с асинхронными запросами и параметрами?

Современная библиотека httpx поддерживает как синхронный, так и асинхронный режимы, а также HTTP/2. Параметры задаются аналогично requests.

import httpx

params = {'country': 'Россия', 'lang': 'ru'}
# Синхронный режим
response = httpx.get('https://api.example.com/locations', params=params)
print(response.url)

# Асинхронный режим
import asyncio
async def fetch():
    async with httpx.AsyncClient() as client:
        resp = await client.get('https://api.example.com/locations', params=params)
        return resp.url
url = asyncio.run(fetch())
print(url)

Ошибка: При передаче параметров в асинхронном клиенте легко забыть про await. Также httpx требует обработки исключений типа httpx.RequestError. Для кириллицы кодирование работает корректно, но если параметры уже содержат символы '=' или '&', они экранируются.

Как отправлять асинхронные запросы с параметрами в aiohttp?

aiohttp - популярная асинхронная библиотека. Параметры передаются через аргумент params.

import aiohttp
import asyncio

async def fetch():
    params = {'id': [1, 2, 3], 'sort': 'desc'}
    async with aiohttp.ClientSession() as session:
        async with session.get('https://api.example.com/data', params=params) as resp:
            print(resp.url)

asyncio.run(fetch())
https://api.example.com/data?id=1&id=2&id=3&sort=desc

Проблема: aiohttp не поддерживает параметр params для POST-запросов с телом формы; для этого используется data. Также если нужно передать параметры во вложенном виде (например, filter[name]=xxx), aiohttp не делает этого автоматически - требуется ручное формирование строки запроса.

Как вручную собрать URL с параметрами для простых случаев?

Когда параметры заранее известны и не содержат специальных символов, можно использовать f-строки или конкатенацию. Этот метод не рекомендуется для production из-за риска некорректного кодирования.

base_url = 'https://api.example.com/items'
params = {'page': '1', 'limit': '20'}
query = '&'.join(f'{k}={v}' for k, v in params.items())
url = base_url + '?' + query
print(url)
https://api.example.com/items?page=1&limit=20

Проблема: Если значения содержат пробелы, кириллицу, символы & или =, строка будет некорректной. Необходимо экранировать вручную через urllib.parse.quote. Также невозможно передать несколько значений с одинаковым ключом без дублирования кода.

Расширенные примеры работы с параметрами

В этом разделе рассматриваются нестандартные сценарии, которые часто встречаются при интеграции с веб-API.

Вложенные параметры (nested query strings)

Некоторые API ожидают параметры вида filter[name]=value. Ни одна из стандартных библиотек не поддерживает это автоматически, поэтому требуется ручное формирование.

Пример
from urllib.parse import urlencode

def nested_params(params, prefix=''):
    items = []
    for key, value in params.items():
        full_key = f'{prefix}[{key}]' if prefix else key
        if isinstance(value, dict):
            items.extend(nested_params(value, full_key).items())
        else:
            items.append((full_key, value))
    return dict(items)

params = {
    'filter': {'name': 'John', 'age': 30},
    'sort': 'name'
}
flat = nested_params(params)
query = urlencode(flat)
print(query)
filter%5Bname%5D=John&filter%5Bage%5D=30&sort=name

Пояснение: Рекурсивно строятся ключи с квадратными скобками. После декодирования %5B - это '[' , %5D - ']'.

Передача списков с разными стилями

Некоторые API поддерживают индексированные списки: filter[0][name]=aaa. Пример построения такой строки:

Пример
from urllib.parse import urlencode

items = [{'name': 'Alice'}, {'name': 'Bob'}]
params = []
for i, item in enumerate(items):
    for k, v in item.items():
        params.append((f'filter[{i}][{k}]', v))
query = urlencode(params)
print(query)
filter%5B0%5D%5Bname%5D=Alice&filter%5B1%5D%5Bname%5D=Bob

Комбинирование параметров с телом POST запроса

Иногда в одном запросе нужно отправить query-параметры в URL и данные формы в теле. Библиотека requests это позволяет:

Пример
import requests

params = {'api_key': '12345'}
data = {'username': 'user', 'password': 'pass'}
response = requests.post('https://example.com/login', params=params, data=data)
print('URL:', response.request.url)
print('Body:', response.request.body)
URL: https://example.com/login?api_key=12345
Body: username=user&password=pass

Обработка кириллицы и спецсимволов в параметрах

При работе с русскими текстами важно следить за кодировкой. Библиотека requests корректно обрабатывает UTF-8, но если сервер ожидает другую кодировку, можно задать параметр params в виде уже закодированных строк.

Пример
import requests
from urllib.parse import quote

# Ручное кодирование в cp1251
value = 'Москва'.encode('cp1251')
params = {'city': value}
# Неправильно: requests воспримет bytes как строку
response = requests.get('https://example.com', params=params)
print(response.url)  # символы будут выглядеть как %XX

# Правильный способ: передать уже закодированную строку
params = {'city': quote('Москва', encoding='cp1251')}
response = requests.get('https://example.com', params=params)
print(response.url)
https://example.com?city=%CC%EE%F1%EA%E2%E0

Использование параметров с сессиями и куками

При создании сессии с помощью requests.Session() параметры передаются аналогично, но можно установить базовые параметры для всех запросов сессии.

Пример
import requests

session = requests.Session()
session.params = {'api_key': 'secret'}
# Теперь все GET запросы через сессию будут автоматически добавлять этот параметр
resp = session.get('https://api.example.com/items')
print(resp.url)  # https://api.example.com/items?api_key=secret

Параметры для пагинации в REST API

Многие API используют параметры page и per_page или offset/limit. Пример цикла для сбора всех страниц:

Пример
import requests

base_url = 'https://api.example.com/users'
all_users = []
page = 1
per_page = 50
while True:
    params = {'page': page, 'per_page': per_page}
    response = requests.get(base_url, params=params)
    data = response.json()
    if not data:  # пустой список
        break
    all_users.extend(data)
    page += 1
print(f'Собрано {len(all_users)} пользователей')

В реальном API может быть ответ с полем `next` или `total_pages` - тогда логика строится на его основе.

Параметры HTTP запроса в Python - comments

En
параметры запроса python (python)