如何在python中发送多部分/表单数据请求?怎么发文件,我懂,但是怎么用这种方法发表单数据就不懂了。


当前回答

为了澄清上面的例子,

即使不需要上传任何文件,也需要使用files参数发送多部分表单POST请求。

文件= {}

不幸的是,这行不通。

您将需要放入一些虚拟值,例如。

files={"foo": "bar"}

当我试图上传文件到Bitbucket的REST API时,我遇到了这个问题,不得不写这个讨厌的东西,以避免可怕的“不支持的媒体类型”错误:

url = "https://my-bitbucket.com/rest/api/latest/projects/FOO/repos/bar/browse/foobar.txt"
payload = {'branch': 'master', 
           'content': 'text that will appear in my file',
           'message': 'uploading directly from python'}
files = {"foo": "bar"}
response = requests.put(url, data=payload, files=files)

:O=

其他回答

基本上,如果你指定了一个files参数(一个字典),那么请求将发送一个多部分的/form-data POST,而不是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--

文件也可以是双值元组的列表,如果你需要排序和/或多个相同名称的字段:

requests.post(
    'http://requestb.in/xucj9exu',
    files=(
        ('foo', (None, 'bar')),
        ('foo', (None, 'baz')),
        ('spam', (None, 'eggs')),
    )
)

如果同时指定了文件和数据,那么将使用什么来创建POST体取决于数据的值。如果data是字符串,则只使用它;否则,将同时使用数据和文件,首先列出数据中的元素。

还有一个优秀的请求-工具带项目,它包括高级的多部件支持。它接受与files形参相同格式的字段定义,但与请求不同,它默认不设置filename形参。此外,它可以从打开的文件对象中传输请求,其中请求将首先在内存中构造请求体:

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形参不同,如果不使用元组,则不会尝试查找默认的文件名值。

自从编写了以前的一些答案之后,请求已经发生了变化。在Github上看看这个问题的更多细节,这条评论是一个例子。

简而言之,files参数接受一个字典,键为表单字段的名称,值为字符串或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

这是你需要上传一个大的单一文件作为多部分formdata的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)

对于服务器端,请检查multer文档:https://github.com/expressjs/multer 这里的字段single('fieldName')用于接受单个文件,如下所示:

var upload = multer().single('fieldName');

即使不需要上传任何文件,也需要使用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可以是a:

2元组(文件名,fileobj) 3-tuple (filename, fileobj, content_type) 4元组(文件名,fileobj, content_type, custom_headers)。

\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \

基于以上,最简单的多部分表单请求,包括要上传的文件和表单字段,将如下所示:

import requests

multipart_form_data = {
    'upload': ('custom_file_name.zip', open('myfile.zip', 'rb')),
    'action': (None, 'store'),
    'path': (None, '/path1')
}

response = requests.post('https://httpbin.org/post', files=multipart_form_data)

print(response.content)

注意None作为纯文本字段的元组中的第一个参数——这是文件名字段的占位符,仅用于文件上传,但对于文本字段,为了提交数据,必须将None作为第一个参数。

具有相同名称的多个字段

如果你需要发布多个具有相同名称的字段,那么你可以将你的有效负载定义为元组的列表(或元组):

multipart_form_data = (
    ('file2', ('custom_file_name.zip', open('myfile.zip', 'rb'))),
    ('action', (None, 'store')),
    ('path', (None, '/path1')),
    ('path', (None, '/path2')),
    ('path', (None, '/path3')),
)

流媒体请求API

如果上面的API对你来说不够python化,那么可以考虑使用requests toolbelt (pip install requests_toolbelt),它是核心请求模块的扩展,提供了对文件上传流的支持,以及可以用来代替文件的MultipartEncoder,它还允许你将负载定义为字典、元组或列表。

MultipartEncoder可以用于有或没有实际上传字段的多部分请求。它必须分配给data参数。

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

multipart_data = MultipartEncoder(
    fields={
            # a file upload field
            'file': ('file.zip', 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'),
           )
    )

我试图用python 3中的请求模块发送一个请求到URL_server。 这对我来说很管用:

# -*- coding: utf-8 *-*
import json, requests

URL_SERVER_TO_POST_DATA = "URL_to_send_POST_request"
HEADERS = {"Content-Type" : "multipart/form-data;"}

def getPointsCC_Function():
  file_data = {
      'var1': (None, "valueOfYourVariable_1"),
      'var2': (None, "valueOfYourVariable_2")
  }

  try:
    resElastic = requests.post(URL_GET_BALANCE, files=file_data)
    res = resElastic.json()
  except Exception as e:
    print(e)

  print (json.dumps(res, indent=4, sort_keys=True))

getPointsCC_Function()

地点:

URL_SERVER_TO_POST_DATA =我们要发送数据的服务器 报头=发送的报头 file_data =发送的参数