How to send a “multipart/form-data” with requests in python?

前端 未结 9 1791
野趣味
野趣味 2020-11-22 01:29

How to send a multipart/form-data with requests in python? How to send a file, I understand, but how to send the form data by this method can not understand.

9条回答
  •  盖世英雄少女心
    2020-11-22 01:53

    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. You are not limited to using actual files in that dictionary, however:

    >>> import requests
    >>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
    >>> response.status_code
    200
    

    and httpbin.org lets you know what headers you posted with; in response.json() we have:

    >>> 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'}
    

    Better still, you can further control the filename, content type and additional headers for each part by using a tuple instead of a single string or bytes object. The tuple is expected to contain between 2 and 4 elements; the filename, the content, optionally a content type, and an optional dictionary of further headers.

    I'd use the tuple form with None as the filename, so that the filename="..." parameter is dropped from the request for those parts:

    >>> 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 can also be a list of two-value tuples, if you need ordering and/or multiple fields with the same name:

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

    If you specify both files and data, then it depends on the value of data what will be used to create the POST body. If data is a string, only it willl be used; otherwise both data and files are used, with the elements in data listed first.

    There is also the excellent requests-toolbelt project, which includes advanced Multipart support. It takes field definitions in the same format as the files parameter, but unlike requests, it defaults to not setting a filename parameter. In addition, it can stream the request from open file objects, where requests will first construct the request body in memory:

    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}
    )
    

    Fields follow the same conventions; use a tuple with between 2 and 4 elements to add a filename, part mime-type or extra headers. Unlike the files parameter, no attempt is made to find a default filename value if you don't use a tuple.

提交回复
热议问题