Подтвердить что ты не робот

Как отправить "multipart/form-data" с запросами в python?

Как отправить multipart/form-data с запросами в python? Как отправить файл, я понимаю, но как отправить данные формы этим методом не могу понять.

4b9b3361

Ответ 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)

Обратите внимание, что вам не нужно явно указывать какой-либо тип контента.

ПРИМЕЧАНИЕ. Хотел прокомментировать один из приведенных выше ответов, но не смог из-за низкой репутации, поэтому подготовил новый ответ здесь.