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

新手上路,请多包涵

如何在 python 中发送 multipart/form-datarequests ?如何发送文件,我明白,但是如何通过这种方法发送表单数据就无法理解。

原文由 agrynchuk 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 496
1 个回答

Basically, if you specify a files parameter (a dictionary), then requests will send a multipart/form-data POST instead of a application/x-www-form-urlencoded POST.您不限于使用该词典中的实际文件,但是:

 >>> 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')),
    )
)

如果同时指定 filesdata ,那么它取决于 data body. 将用于创建 POST如果 data 是一个字符串,则只使用它;否则同时使用 datafiles ,首先列出 data 中的元素。

还有优秀的 requests-toolbelt 项目,其中包括 高级多部分支持。它采用与 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 值。

原文由 Martijn Pieters 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题