Wie versendet man eine multipart/form-data
avec requests
in Python? Wie man eine Datei sendet, verstehe ich, aber wie man die Formulardaten durch diese Methode senden kann nicht verstehen.
Antworten
Zu viele Anzeigen?Grundsätzlich gilt: Wenn Sie eine files
Parameter (ein Wörterbuch), dann requests
sendet eine multipart/form-data
POST anstelle einer 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 lässt Sie wissen, 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'}
Noch besser ist, dass Sie den Dateinamen, den Inhaltstyp und die zusätzlichen Kopfzeilen für jeden Teil weiter steuern können, indem Sie ein Tupel anstelle einer einzelnen Zeichenkette oder eines Byte-Objekts verwenden. Das Tupel sollte zwischen 2 und 4 Elemente enthalten: den Dateinamen, den Inhalt, optional einen Inhaltstyp und ein optionales Wörterbuch mit weiteren Kopfzeilen.
Ich würde die Tupelform verwenden mit None
als Dateinamen, so dass die filename="..."
wird für diese Teile aus der Anfrage gestrichen:
>>> 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 Tupeln mit zwei Werten sein, wenn Sie eine Reihenfolge und/oder mehrere Felder mit demselben Namen benötigen:
requests.post(
'http://requestb.in/xucj9exu',
files=(
('foo', (None, 'bar')),
('foo', (None, 'baz')),
('spam', (None, 'eggs')),
)
)
Wenn Sie beides angeben files
y data
dann hängt es von der valeur de data
was für die Erstellung des POST-Bodys verwendet wird. Wenn data
eine Zeichenkette ist, wird nur diese verwendet; andernfalls werden beide data
y files
verwendet werden, wobei die Elemente in data
zuerst aufgeführt.
Außerdem gibt es die ausgezeichnete requests-toolbelt
Projekt, das Folgendes umfasst erweiterte Multipart-Unterstützung . Es nimmt Felddefinitionen im gleichen Format wie das Programm files
Parameter, aber im Gegensatz zu requests
wird standardmäßig kein Parameter für den Dateinamen gesetzt. Darüber hinaus kann es die Anforderung von offenen Dateiobjekten streamen, wobei requests
baut zunächst den Anfragekörper im Speicher auf:
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}
)
Felder folgen den gleichen Konventionen; verwenden Sie ein Tupel mit 2 bis 4 Elementen, um einen Dateinamen, einen Teil des Mime-Typs oder zusätzliche Kopfzeilen hinzuzufügen. Anders als die files
Parameter wird nicht versucht, einen Standard filename
Wert, wenn Sie kein Tupel verwenden.
Die Anforderungen haben sich geändert, seit einige der vorherigen Antworten geschrieben wurden. Werfen Sie einen Blick auf dieses Issue auf Github für weitere Einzelheiten und dieser Kommentar für ein Beispiel.
Kurz gesagt, die files
ein Wörterbuch, wobei der Schlüssel der Name des Formularfeldes und der Wert entweder eine Zeichenkette oder ein Tupel der Länge 2, 3 oder 4 ist, wie im Abschnitt POST einer Multipart-kodierten Datei im Schnellstart für Anfragen:
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
In der obigen Darstellung ist das Tupel wie folgt zusammengesetzt:
(filename, data, content_type, headers)
Wenn der Wert nur eine Zeichenkette ist, ist der Dateiname derselbe wie der Schlüssel, wie im Folgenden dargestellt:
>>> 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 ist und der erste Eintrag lautet None
wird die Eigenschaft filename nicht berücksichtigt:
>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}
Content-Disposition: form-data; name="obvius_session_id"
Content-Type: application/octet-stream
72c2b6f406cdabd578c5fd7598557c52
Sie müssen die files
Parameter zum Senden einer mehrteiligen POST-Anfrage sogar wenn Sie keine Dateien hochladen müssen.
Vom Original 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 lautet: file-tuple
can be a
:
2-tuple
( Dateiname, fileobj )3-tuple
( filename, fileobj, content_type )4-tuple
( filename, fileobj, content_type, custom_headers ).
Was vielleicht nicht offensichtlich ist, ist, dass
fileobj
kann sein entweder ein tatsächliches Dateiobjekt beim Umgang mit Dateien, OR eine Zeichenkette, wenn es sich um reine Textfelder handelt.
Die einfachste mehrteilige Formularanforderung, die sowohl hochzuladende Dateien als auch Formularfelder enthält, sieht wie folgt aus:
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)
Beachten Sie die None
als erstes Argument im Tupel für reine Textfelder - dies ist ein Platzhalter für das Feld filename, das nur für Datei-Uploads verwendet wird, aber für Textfelder, die None
als erster Parameter ist erforderlich, damit die Daten übermittelt werden können.
Mehrere Felder mit demselben Namen
Wenn Sie mehrere Felder mit demselben Namen buchen müssen, können Sie anstelle eines Wörterbuchs Ihre Nutzlast als Liste (oder Tupel) von Tupeln definieren:
multipart_form_data = (
('file2', ('custom_file_name.zip', open('myfile.zip', 'rb'))),
('action', (None, 'store')),
('path', (None, '/path1')),
('path', (None, '/path2')),
('path', (None, '/path3')),
)
Streaming-Anfragen API
Wenn Ihnen die obige API nicht pythonisch genug ist, dann sollten Sie die Verwendung von Anfragen Werkzeuggürtel ( pip install requests_toolbelt
), die eine Erweiterung des zentrale Anliegen Modul, das Unterstützung für das Hochladen von Dateien bietet, sowie das MultipartEncoder die anstelle von files
und können die Nutzlast auch als Wörterbuch, Tupel oder Liste definieren.
MultipartEncoder
kann sowohl für mehrteilige Anfragen mit oder ohne tatsächliche Upload-Felder verwendet werden. Sie muss dem data
Parameter.
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 wenn die Reihenfolge der Formularfelder wichtig ist, können Sie statt eines Wörterbuchs ein Tupel oder eine Liste verwenden:
multipart_data = MultipartEncoder(
fields=(
('action', 'ingest'),
('item', 'spam'),
('item', 'sausage'),
('item', 'eggs'),
)
)
Hier ein einfaches Code-Snippet zum Hochladen einer einzelnen Datei mit zusätzlichen Parametern unter Verwendung von Anfragen:
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 nicht explizit einen Inhaltstyp angeben müssen.
HINWEIS: Ich wollte eine der obigen Antworten kommentieren, konnte es aber wegen des geringen Ansehens nicht, also habe ich hier eine neue Antwort verfasst.
Durch die Angabe einer files
Parameter in der POST
Anfrage, die Content-Type
der Anfrage ist automatisch eingestellt auf multipart/form-data
(gefolgt von der boundary
Zeichenfolge, die zur Trennung der einzelnen Körperteile in der mehrteiligen Nutzlast verwendet wird), ob Sie nur files
, oder form-data
y files
zur gleichen Zeit (also eine sollte nicht versuchen, die Content-Type
manuell in diesem Fall). Wenn hingegen nur form-data
gesendet wurden, die Content-Type
würde automatisch auf application/x-www-form-urlencoded
.
Sie können die Content-Type
Header der Anfrage, um dies anhand des folgenden Beispiels zu überprüfen, das zeigt, wie man mehrere Dateien (oder eine einzige Datei) mit (optional) demselben key
(d.h., 'files'
im folgenden Fall), sowie mit optionalen form-data
(d.h., data=data
im untenstehenden Beispiel). Die Dokumentation über die POST
einfach und mehrfach files
kann gefunden werden aquí y aquí . Falls Sie große Dateien hochladen müssen, ohne sie in den Speicher zu lesen, sehen Sie sich folgende Seiten an Streaming-Uploads . Für die Serverseite - falls diese benötigt wird - schauen Sie bitte unter diese Antwort , aus dem der nachstehende Codeausschnitt stammt, und der die FastAPI Web-Framework.
Ejemplo
import requests
url = 'http://127.0.0.1:8000/submit'
files = [('files', open('a.txt', 'rb')), ('files', open('b.txt', 'rb'))]
#file = {'file': open('a.txt','rb')} # to send a single file
data ={"name": "foo", "point": 0.13, "is_accepted": False}
r = requests.post(url=url, data=data, files=files)
print(r.json())
print(r.request.headers['content-type'])
- See previous answers
- Weitere Antworten anzeigen