it-swarm.com.de

Wie sendet man "Multipart/Form-Daten" mit Anfragen in Python?

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

148
agrynchuk

Wenn Sie einen files-Parameter (ein Wörterbuch) angeben, sendet requests grundsätzlich einen multipart/form-data POST anstelle eines application/x-www-form-urlencoded-POST. Sie sind jedoch nicht darauf beschränkt, tatsächliche Dateien in diesem Wörterbuch zu verwenden:

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

und httpbin.org informiert Sie darüber, mit welchen Headern Sie gepostet haben. in response.json() haben wir:

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

Besser noch, Sie können den Dateinamen, den Inhaltstyp und zusätzliche Kopfzeilen für jeden Teil weiter steuern, indem Sie anstelle eines einzelnen String- oder Byte-Objekts ein Tuple verwenden. Es wird erwartet, dass der Tupel zwischen 2 und 4 Elemente enthält. den Dateinamen, den Inhalt, optional einen Inhaltstyp und ein optionales Wörterbuch mit weiteren Kopfzeilen.

Ich würde das Tupel-Formular mit None als Dateinamen verwenden, sodass der filename="..."-Parameter aus der Anforderung für diese Teile gelöscht wird:

>>> 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 kann auch eine Liste von zwei Werten sein, wenn Sie eine Bestellung und/oder mehrere Felder mit demselben Namen benötigen:

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

Wenn Sie sowohl files als auch data angeben, hängt es vom value von data ab, was zur Erstellung des POST - Body verwendet wird. Wenn data eine Zeichenfolge ist, wird nur diese verwendet. Andernfalls werden sowohl data als auch files verwendet, wobei die Elemente in data zuerst aufgeführt werden.

Es gibt auch das hervorragende requests-toolbelt-Projekt, das fortgeschrittene Multipart-Unterstützung beinhaltet. Es sind Felddefinitionen im gleichen Format wie der Parameter files erforderlich. Im Gegensatz zu requests wird jedoch kein Dateiname-Parameter festgelegt. Außerdem kann die Anforderung von geöffneten Dateiobjekten gestreamt werden, wobei requests den Anforderungshauptteil zuerst im Speicher erstellt.

97
Martijn Pieters

Seit dem Schreiben der vorherigen Antworten haben sich die Anforderungen geändert. Schauen Sie sich den Thread bug unter Github an für weitere Details und diesen Kommentar für ein Beispiel.

Kurz gesagt, der files-Parameter benötigt eine dict, wobei der Schlüssel der Name des Formularfelds ist und der Wert entweder eine Zeichenfolge oder ein 2, 3 oder 4-Tupel ist, wie in Abschnitt POST a beschrieben Multipart-kodierte Datei im Schnellstart der Anforderung:

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

Der Tupel setzt sich wie folgt zusammen:

(filename, data, content_type, headers)

Wenn der Wert nur eine Zeichenfolge ist, stimmt der Dateiname mit dem Schlüssel überein.

>>> 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 Tuple ist und der erste Eintrag None ist, wird die Eigenschaft "Dateiname" nicht eingeschlossen:

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

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

72c2b6f406cdabd578c5fd7598557c52
92
runejuhl

Sie müssen den Parameter files verwenden, um ein multipart-Formular POST - Anforderung gerade zu senden, wenn Sie keine Dateien hochladen müssen. 

Von den ursprünglichen Anfragen 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.

Der relevante Teil ist: file-Tuple can be a2-Tuple, 3-Tupleor a4-Tuple.

Basierend auf den obigen Angaben sieht die einfachste mehrteilige Formularanforderung, die sowohl hochzuladende Dateien als auch Formularfelder enthält, folgendermaßen aus:

multipart_form_data = {
    'file2': ('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)

{Beachten Sie die None als erstes Argument im Tuple für Klartextfelder. Dies ist ein Platzhalter für das Dateinamensfeld, das nur für das Hochladen von Dateien verwendet wird. Für Textfelder muss jedoch None als erster Parameter übergeben werden die zu übermittelnden Daten.


Wenn diese API für Sie nicht Pythonic genug ist oder wenn Sie mehrere Felder mit demselben Namen bereitstellen müssen, sollten Sie die Verwendung von request toolbelt (pip install requests_toolbelt) in Erwägung ziehen. Dies ist eine Erweiterung der core-Anfragen . Dieses Modul bietet Unterstützung für das Hochladen von Dateien sowie den MultipartEncoder , der anstelle von files verwendet werden kann, und der Parameter sowohl als Wörterbücher als auch als Tupel akzeptiert.

MultipartEncoder kann sowohl für mehrteilige Anforderungen mit als auch ohne tatsächliche Upload-Felder verwendet werden. Es 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.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})

Wenn Sie mehrere Felder mit demselben Namen senden müssen oder die Reihenfolge der Formularfelder wichtig ist, kann anstelle eines Wörterbuchs ein Tupel oder eine Liste verwendet werden, d. H .:

multipart_data = MultipartEncoder(
    fields=(
            ('action', 'ingest'), 
            ('item', 'spam'),
            ('item', 'sausage'),
            ('item', 'eggs'),
           )
    )
47
ccpizza

Sie müssen das name-Attribut der Upload-Datei verwenden, die sich im HTML-Code der Site befindet. Beispiel:

autocomplete="off" name="image">

Sie sehen name="image">? Sie finden es im HTML-Code einer Website zum Hochladen der Datei. Sie müssen es verwenden, um die Datei mit Multipart/form-data hochzuladen.

skript:

import requests

site = 'https://prnt.sc/upload.php' # the site where you upload the file
filename = 'image.jpg'  # name example

Fügen Sie hier anstelle von image den Namen der Upload-Datei in HTML hinzu

up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}

Wenn für das Hochladen ein Klick auf die Schaltfläche zum Hochladen erforderlich ist, können Sie Folgendes verwenden:

data = {
     "Button" : "Submit",
}

Starten Sie dann die Anfrage

request = requests.post(site, files=up, data=data)

Und fertig, die Datei wurde erfolgreich hochgeladen 

4
Skiller Dz

Hier ist das Python-Snippet, das Sie benötigen, um eine große einzelne Datei als mehrteilige Formulardaten hochzuladen. Mit NodeJs Multer-Middleware auf der Serverseite. 

import requests
latest_file = 'path/to/file'
url = "http://httpbin.org/apiToUpload"
files = {'fieldName': open(latest_file, 'rb')}
r = requests.put(url, files=files)

Für die Serverseite lesen Sie bitte die Dokumentation des Mulder unter: https://github.com/expressjs/multer Hier wird das Feld single ('fieldName') verwendet, um eine einzige Datei zu akzeptieren, wie in

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

Hier ist das einfache Code-Snippet zum Hochladen einer einzelnen Datei mit zusätzlichen Parametern mithilfe von Anforderungen:

url = 'https://<file_upload_url>'
fp = '/Users/jainik/Desktop/data.csv'

files = {'file': open(fp, 'rb')}
payload = {'file_id': '1234'}

response = requests.put(url, files=files, data=payload, verify=False)

Bitte beachten Sie, dass Sie keinen Inhaltstyp explizit angeben müssen.

HINWEIS: Wollte zu einer der obigen Antworten einen Kommentar abgeben, konnte aber wegen mangelnder Reputation hier keine neue Antwort verfassen.

0
Jainik