一尘不染

如何在python中发送带有请求的“ multipart / form-data”?

python

如何multipart/form-data在python中发送带有请求的请求?我了解如何发送文件,但是通过这种方法如何发送表单数据尚不清楚。


阅读 784

收藏
2020-02-13

共1个答案

一尘不染

基本上,如果你指定files参数(字典),requests则将发送multipart/form-dataPOST而不是application/x-www-form-urlencodedPOST。你不限于在该词典中使用实际文件,但是:

>>> 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')),
        ('foo', (None, 'baz')),
        ('spam', (None, 'eggs')),
    )
)

如果同时指定files和data,然后它取决于价值的data东西将被用于创建POST体。如果data是字符串,则仅使用它将;否则data,files都使用和,并data首先列出元素。

还有一个出色的requests-toolbelt项目,其中包括高级的Multipart支持。它采用与files参数格式相同的字段定义,但是与不同requests,它默认不设置文件名参数。此外,它可以从打开的文件对象流式传输请求,requests首先将在内存中构造请求主体:

from requests_toolbelt.multipart.encoder import MultipartEncoder

mp_encoder = MultipartEncoder(
    fields={
        'foo': 'bar',
        # plain file object, no filename or mime type produces a
        # Content-Disposition header with just the part name
        'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'),
    }
)
r = requests.post(
    'http://httpbin.org/post',
    data=mp_encoder,  # The MultipartEncoder is posted as data, don't use files=...!
    # The MultipartEncoder provides the content-type header with the boundary:
    headers={'Content-Type': mp_encoder.content_type}
)

字段遵循相同的约定;使用包含2到4个元素的元组来添加文件名,部分mime类型或额外的标头。与files参数不同,filename如果不使用元组,则不会尝试查找默认值。

2020-02-13