Как отправить multipart/form-data
с запросами в python? Как отправить файл, я понимаю, но как отправить данные формы этим методом не могу понять.
Как отправить "multipart/form-data" с запросами в python?
Ответ 1
По сути, если вы укажете параметр files
(словарь), то requests
будут отправлять POST multipart/form-data
вместо POST application/x-www-form-urlencoded
. Вы не ограничены использованием реальных файлов в этом словаре, однако:
>>> import requests
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
>>> response.status_code
200
и httpbin.org позволяет узнать, с какими заголовками вы разместили; в response.json()
мы имеем:
>>> from pprint import pprint
>>> pprint(response.json()['headers'])
{'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Content-Length': '141',
'Content-Type': 'multipart/form-data; '
'boundary=c7cbfdd911b4e720f1dd8f479c50bc7f',
'Host': 'httpbin.org',
'User-Agent': 'python-requests/2.21.0'}
Более того, вы можете дополнительно контролировать имя файла, тип содержимого и дополнительные заголовки для каждой части, используя кортеж вместо единственной строки или объекта байтов. Ожидается, что кортеж будет содержать от 2 до 4 элементов; имя файла, содержимое, необязательно тип содержимого и необязательный словарь дополнительных заголовков.
Я бы использовал эту форму кортежа с None
качестве имени файла, так что параметр filename="..."
удаляется из запроса на эти части:
>>> files = {'foo': 'bar'}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content-Disposition: form-data; name="foo"; filename="foo"
bar
--bb3f05a247b43eede27a124ef8b968c5--
>>> files = {'foo': (None, 'bar')}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content-Disposition: form-data; name="foo"
bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--
files
также могут быть списком кортежей с двумя значениями, если вам нужен порядок и/или несколько полей с одинаковым именем:
requests.post('http://requestb.in/xucj9exu', files=(('foo', (None, 'bar')), ('spam', (None, 'eggs'))))
Если вы указываете и files
и data
, то это зависит от значения data
которые будут использоваться для создания тела POST. Если data
- строка, будет использоваться только она; в противном случае используются как data
и files
, причем элементы data
перечисляются первыми.
Существует также превосходный проект requests-toolbelt
, который включает расширенную поддержку Multipart. Он принимает определения полей в том же формате, что и параметр files
, но в отличие от requests
он по умолчанию не устанавливает параметр имени файла. Кроме того, он может передавать запрос от открытых файловых объектов, где requests
сначала создают тело запроса в памяти.
Ответ 2
Поскольку предыдущие ответы были написаны, запросы изменились. Посмотрите на ошибку в Github для более подробной информации и этот комментарий для примера.
Вкратце, параметр files принимает значение dict
, при этом ключ является именем поля формы, а значение является строкой или кортежем длиной 2, 3 или 4 длины, как описано в разделе POST файл с многостраничным кодированием в запросах быстрого запуска:
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
В приведенном выше примере кортеж состоит из следующего:
(filename, data, content_type, headers)
Если значение является просто строкой, имя файла будет таким же, как и ключ, как показано ниже:
>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'}
Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id"
Content-Type: application/octet-stream
72c2b6f406cdabd578c5fd7598557c52
Если значение является кортежем, а первая запись None
, свойство filename не будет включено:
>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}
Content-Disposition: form-data; name="obvius_session_id"
Content-Type: application/octet-stream
72c2b6f406cdabd578c5fd7598557c52
Ответ 3
Вам нужно использовать параметр files
чтобы отправить POST-запрос из нескольких частей, даже если вам не нужно загружать какие-либо файлы.
Из исходного источника запросов:
def request(method, url, **kwargs):
"""Constructs and sends a :class:'Request <Request>'.
...
:param files: (optional) Dictionary of '''name': file-like-objects''
(or ''{'name': file-tuple}'') for multipart encoding upload.
''file-tuple'' can be a 2-tuple ''('filename', fileobj)'',
3-tuple ''('filename', fileobj, 'content_type')''
or a 4-tuple ''('filename', fileobj, 'content_type', custom_headers)'',
where '''content-type''' is a string
defining the content type of the given file
and ''custom_headers'' a dict-like object
containing additional headers to add for the file.
Соответствующая часть: file-tuple can be a
2-tuple
, 3-tuple
or a
4-tuple
.
Исходя из вышеизложенного, простейший многочастный запрос формы, который включает в себя как файлы для загрузки, так и поля формы, будет выглядеть следующим образом:
multipart_form_data = {
'file2': ('custom_file_name.zip', open('myfile.zip', 'rb')),
'action': ('', 'store'),
'path': ('', '/path1')
}
response = requests.post('https://httpbin.org/post', files=multipart_form_data)
print(response.content)
☝ Обратите внимание на пустую строку в качестве первого аргумента в кортеже для текстовых полей - это заполнитель для поля имени файла, который используется только для загрузки файла, но для текстовых полей пустой заполнитель все еще требуется для того, чтобы данные были отправлены,
Если этот API не является достаточно питоническим для вас, или если вам нужно опубликовать несколько полей с одним и тем же именем, рассмотрите возможность использования инструментального пояса запросов (
pip install requests_toolbelt
reports_toolbelt), который является расширением модуля основных запросов, который обеспечивает поддержку потоковой передачи файлов в виде а также MultipartEncoder, который можно использовать вместо files
и который принимает параметры как словари, так и кортежи. MultipartEncoder
может использоваться как для составных запросов с фактическими полями загрузки, так и без них. Он должен быть назначен параметру data
.
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
multipart_data = MultipartEncoder(
fields={
# a file upload field
'file': ('file.py', open('file.zip', 'rb'), 'text/plain')
# plain text fields
'field0': 'value0',
'field1': 'value1',
}
)
response = requests.post('http://httpbin.org/post', data=multipart_data,
headers={'Content-Type': multipart_data.content_type})
Если вам нужно отправить несколько полей с одним и тем же именем или если важен порядок полей формы, тогда вместо словаря можно использовать кортеж или список, то есть:
multipart_data = MultipartEncoder(
fields=(
('action', 'ingest'),
('item', 'spam'),
('item', 'sausage'),
('item', 'eggs'),
)
)
Ответ 4
Вам нужно использовать атрибут name
загружаемого файла, который находится в HTML-сайте. Пример:
autocomplete="off" name="image">
Вы видите name="image">
? Вы можете найти его в HTML-сайте для загрузки файла. Вы должны использовать его для загрузки файла с помощью Multipart/form-data
сценарий:
import requests
site = 'https://prnt.sc/upload.php' # the site where you upload the file
filename = 'image.jpg' # name example
Здесь, вместо изображения, добавьте имя загружаемого файла в HTML
up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}
Если для загрузки требуется нажать кнопку для загрузки, вы можете использовать это:
data = {
"Button" : "Submit",
}
Затем запустите запрос
request = requests.post(site, files=up, data=data)
И сделано, файл загружен успешно
Ответ 5
Вот фрагмент кода Python, который вам нужен для загрузки одного большого файла в виде составных данных. С промежуточным ПО NodeJs Multer, работающим на стороне сервера.
import requests
latest_file = 'path/to/file'
url = "http://httpbin.org/apiToUpload"
files = {'fieldName': open(latest_file, 'rb')}
r = requests.put(url, files=files)
Со стороны сервера, пожалуйста, проверьте документацию по адресу: https://github.com/expressjs/multer, здесь одиночное поле ('fieldName') используется для приема одного файла, как в:
var upload = multer().single('fieldName');
Ответ 6
Вот простой фрагмент кода для загрузки одного файла с дополнительными параметрами с использованием запросов:
url = 'https://<file_upload_url>'
fp = '/Users/jainik/Desktop/data.csv'
files = {'file': open(fp, 'rb')}
payload = {'file_id': '1234'}
response = requests.put(url, files=files, data=payload, verify=False)
Обратите внимание, что вам не нужно явно указывать какой-либо тип контента.
ПРИМЕЧАНИЕ. Хотел прокомментировать один из приведенных выше ответов, но не смог из-за низкой репутации, поэтому подготовил новый ответ здесь.