Ошибка "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported" после отправки post запроса

Пробовал использовать этот код:

import requests

r = requests.post("https://job.сайт.ru/auth/self-employed/login", {"phone":"+12345678910"})
print(r.status_code)
print(r.text)

но выходит ошибка:

{"systemMessage":"Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported","message":"Упс! Что-то пошло не так...","errors":{}}

Как можно это исправить? Как я понял он ругается о том что кодировка UTF-8 не поддерживается, но к сожалению я не знаю как это исправить

:authority:job.сайт.ru
:method:POST
:path:/auth/self-employed/login
:scheme:https
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:ru,en;q=0.9,ko;q=0.8

это если пользоваться сайтом и сделать этот же запрос через кнопки на сайте


Ответы (1 шт):

Автор решения: insolor

tl;dr. Нужно передать данные через параметр json, а не просто вторым параметром:

r = requests.post(url, json={"phone":"+12345678910"})

Ошибка не в кодировке, а в ContentType в целом: application/x-www-form-urlencoded - это тип контента, который отправляется из html форм, данные при этом передаются в теле запроса, но не в виде json, а одной строкой парами ключ=значение (вида field1=value1&field2=value2, см. пример). Сервер вам отвечает, что такой тип запроса не поддерживается.

Скорее всего вам нужно передавать данные как json, а не в формате x-www-form-urlencoded.

Тип контента x-www-form-urlencoded стал из-за того, что вы передали словарь просто вторым параметром без указания имени параметра, и он попал в параметр data. Библиотека requests при получении словаря или списка кортежей в этом параметре считает, что вы пытаетесь передать данные в виде x-www-form-urlencoded. См. документацию.

Т.е. такой запрос:

r = requests.post(url, {"phone":"+12345678910"})

это то же самое что

r = requests.post(url, data={"phone":"+12345678910"})

и в итоге то же самое что

r = requests.post(url, data="phone=%2B12345678910")

Плюс превратился в %2B в результате url-кодирования (см. википедию).

Как посмотреть фактическое тело запроса:

from requests import Request

request = Request("POST", url="https://job.сайт.ru/auth/self-employed/login", data={"phone":"+12345678910"}).prepare()
print(request.body)

Вывод

phone=%2B12345678910

Чтобы преобразовать словарь в json и передать его в теле запроса, нужно использовать параметр json:

r = requests.post(url, json={"phone":"+12345678910"})

либо передать уже сконвертированный в строку (сериализованный) json в параметр data, тогда это строка будет передана в теле запроса как есть:

r = requests.post(url, data=json.dumps({"phone":"+12345678910"}))

Ну и итоговое тело запроса можно проверить:

from requests import Request

request = Request("POST", url="https://job.сайт.ru/auth/self-employed/login", json={"phone":"+12345678910"}).prepare()
print(request.body)

Вывод:

b'{"phone": "+12345678910"}'

b и одинарные кавычки просто обозначают что передаются бинарные данные, в самом теле запроса они не передаются.

→ Ссылка