2012-09-12 2 views
104

Wie senden Sie eine multipart/form-data mit Anfragen in Python? Wie man eine Datei sendet, verstehe ich, aber wie man die Formulardaten mit dieser Methode senden kann nicht verstehen.Wie senden Sie ein "multipart/form-data" mit Anfragen in Python?

+0

Ihre Frage ist nicht wirklich klar. Was willst du erreichen? Möchten Sie "multipart/form-data" ohne Datei-Upload im Formular senden? –

+2

Die Tatsache, dass 'files' Parameter verwendet wird, um beides zu tun, ist eine sehr schlechte API. Ich habe das Problem mit dem Titel [Senden von mehrteiligen Daten - wir brauchen eine bessere API] (https://github.com/kennethreitz/requests/issues/935) angesprochen, um dies zu beheben. Wenn Sie zustimmen, dass die Verwendung des Parameters 'files' zum Senden von Multitpart-Daten bestenfalls irreführend ist, bitten Sie bitte, die API in dem obigen Problem zu ändern. –

+0

@ PiotrDobrogost dieses Problem ist geschlossen. Ermutigen Sie die Leute nicht dazu, sich zu geschlossenen oder relevanten Themen zu äußern. –

Antwort

81

Grundsätzlich, wenn Sie einen files Parameter (ein Wörterbuch) angeben, dann einen multipart/form-data POST anstelle eines application/x-www-form-urlencoded POST senden requests wird. Sie sind nicht zu dem tatsächlichen Dateien in diesem Wörterbuch beschränkt, jedoch:

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

und httpbin.org lassen Sie wissen, was Header, die Sie mit gebucht; in response.json() haben wir:

>>> from pprint import pprint 
>>> pprint(response.json()['headers']) 
{u'Accept': u'*/*', 
u'Accept-Encoding': u'gzip, deflate, compress', 
u'Connection': u'close', 
u'Content-Length': u'141', 
u'Content-Type': u'multipart/form-data; boundary=33b4531a79be4b278de5f5688fab7701', 
u'Host': u'httpbin.org', 
u'User-Agent': u'python-requests/2.2.1 CPython/2.7.6 Darwin/13.2.0', 
u'X-Request-Id': u'eaf6baf8-fc3d-456b-b17d-e8219ccef1b1'} 

files auch eine Liste von zwei Wertetupeln sein kann, wenn Sie Bestellung und/oder mehrere Felder mit demselben Namen:

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

Wenn Sie sowohl files angeben und data, dann hängt es von dem Wert von data ab, was verwendet wird, um den POST-Körper zu erstellen. Wenn data eine Zeichenfolge ist, wird sie nur verwendet; andernfalls werden sowohl data als auch files verwendet, wobei die Elemente in data zuerst aufgeführt werden.

+4

Dies wird jedes Ding kodieren, das als Dateiparameter in der Multipart-Kodierung an 'files' gesendet wird. Dies erzeugt kein strenges Formular, sondern ein Formular mit allen Dateiparametern. Siehe [this] (https://github.com/kennethreitz/requests/issues/1081) als Referenz. –

+0

@ sigmavirus24: Die Anfrage-API hat sich weiterentwickelt, seit ich das gepostet habe; Lassen Sie mich untersuchen, ob dies jetzt ein Update benötigt. In jedem Fall muss diese Ecke der API [seit einiger Zeit überarbeitet werden] (https://github.com/kennethreitz/requests/issues/935). –

+0

entschuldigt. StackOverflow bringt das an die Spitze und ich vergesse die Reorganisationsfragen und ich muss mir die beantworteten/gefragten Daten anschauen. –

68

Da die vorherigen Antworten geschrieben wurden, haben sich die Anforderungen geändert. Werfen Sie einen Blick auf die bug thread at Github für weitere Details und this comment für ein Beispiel.

Kurz gesagt, der Parameter files nimmt eine dict mit dem Schlüssel der Name des Formularfelds und der Wert ist entweder eine Zeichenfolge oder ein 2, 3 oder 4-Länge Tupel, wie im Abschnitt POST a Multipart-Encoded File in den Anfragen beschrieben Schnellstart:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})} 

in dem obigen wird das Tupel wie folgt zusammen:

(filename, data, content_type, headers) 

Wenn der Wert nur eine Zeichenfolge ist, wird der Dateiname der gleiche wie der Schlüssel sein, wie in der folgenden:

>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'} 

Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id" 
Content-Type: application/octet-stream 

72c2b6f406cdabd578c5fd7598557c52 

Wenn der Wert ein Tupel und der erste Eintrag None die Dateiname-Eigenschaft nicht enthalten sein:

>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')} 

Content-Disposition: form-data; name="obvius_session_id" 
Content-Type: application/octet-stream 

72c2b6f406cdabd578c5fd7598557c52 
+1

Was ist, wenn Sie den Namen und den Dateinamen unterscheiden müssen, aber mehrere Felder mit demselben Namen haben? – Michael

+1

Ich habe ein ähnliches Problem wie @Michael. Kannst du dir die Frage ansehen und etwas vorschlagen? [link] (http://stackoverflow.com/questions/30683352/how-to-upload-multipart-encode-file-and-form-data-as-a-payload) – Shaardool

+0

hat jemand dieses Problem mit mehreren Feldern gelöst mit dem gleichen Namen? – user3131037

27

Sie müssen die files Parameter verwenden, um eine mehrteilige Form POST-Anfrage zu senden, auch wenn Sie Sie müssen keine Dateien hochladen.

Von der ursprünglichen requests Quelle:

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. 

Die einfachste mehrteiliger Form Anforderung, die beiden Dateien Felder zum Hochladen und Form enthält werden wie folgt aussehen:

multipart_form_data = { 
    'file1': open('myfile.zip', 'rb'), 
    '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) 

Beachten Sie die leere Zeichenfolge als das erste Element im Tupel für Nur-Text-Felder - dies ist ein Platzhalter für das Feld Dateiname, das nur für Datei-Uploads verwendet wird, aber für das Textfeld muss der leere Platzhalter in der Reihenfolge vorhanden sein für die Daten eingereicht werden.


Wenn diese API nicht pythonic genug für Sie ist, oder wenn Sie mehrere Felder, die mit dem gleichen Namen schreiben müssen, sollten Sie dann mit requests toolbelt ( pip install requests_toolbelt), die eine Erweiterung des core requests Modul ist die Unterstützung für die Datei zur Verfügung stellt Upload-Streaming sowie die MultipartEncoder, die anstelle von files verwendet werden kann, und die Parameter sowohl als Wörterbücher und Tupel akzeptiert.

MultipartEncoder kann sowohl für mehrteilige Anfragen mit oder ohne tatsächliche Upload-Felder verwendet werden. Sie muss dem Parameter data zugewiesen werden.

import requests 
from requests_toolbelt.multipart.encoder import MultipartEncoder 

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

Wenn Sie mehrere Felder mit demselben Namen zu schicken, oder wenn Auftrag von Formularfeldern wichtig ist, dann ein Tupel oder eine Liste kann anstelle eines Wörterbuchs verwendet werden, das heißt:

multipart_data = MultipartEncoder(
    fields=(
      ('action', 'store'), 
      ('path', '/path1'), 
      ('path', '/path2'), 
      ('path', '/path3'), 
      ) 
    ) 
+0

Vielen Dank dafür. Die Reihenfolge der Schlüssel war mir wichtig und das half sehr. – Splendor

+0

Erstaunlich. Unerklärlicherweise benötigt eine API, mit der ich arbeite, zwei verschiedene Werte für denselben Schlüssel. Das ist großartig. Vielen Dank. – ajon

+0

@ccpizza, was eigentlich diese Zeile bedeutet? > "('file.py', öffnen ('file.py', 'rb'), 'text/plain')". Es funktioniert nicht für mich :( –

Verwandte Themen