Comment envoyer un multipart/form-data
avec des requêtes en python? Comment envoyer un fichier, je comprends, mais comment envoyer les données du formulaire par cette méthode ne peut pas comprendre.
En gros, si vous spécifiez un paramètre files
(un dictionnaire), alors requests
enverra un multipart/form-data
POST au lieu d'un application/x-www-form-urlencoded
POST. Vous n'êtes pas limité à utiliser des fichiers réels dans ce dictionnaire, cependant:
>>> import requests
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
>>> response.status_code
200
et httpbin.org vous permet de savoir avec quels en-têtes vous avez posté; en response.json()
nous avons:
>>> 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'}
Mieux encore, vous pouvez contrôler davantage le nom de fichier, le type de contenu et des en-têtes supplémentaires pour chaque partie en utilisant un tuple au lieu d'une seule chaîne ou d'un seul octet. Le tuple devrait contenir entre 2 et 4 éléments; le nom de fichier, le contenu, éventuellement un type de contenu et un dictionnaire optionnel d'en-têtes supplémentaires.
J'utiliserais le formulaire Tuple avec None
comme nom de fichier, de sorte que le paramètre filename="..."
soit supprimé de la demande de ces parties:
>>> 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
peut aussi être une liste de n-uplets à deux valeurs, si vous avez besoin de commander et/ou plusieurs champs du même nom:
requests.post('http://requestb.in/xucj9exu', files=(('foo', (None, 'bar')), ('spam', (None, 'eggs'))))
Si vous spécifiez à la fois files
et data
, tout dépend du value de data
qui sera utilisé pour créer le corps POST. Si data
est une chaîne, seule celle-ci sera utilisée; sinon, data
et files
sont utilisés, les éléments de data
étant répertoriés en premier.
Il y a aussi l'excellent projet requests-toolbelt
, qui inclut support multipart avancé . Il prend les définitions de champ dans le même format que le paramètre files
, mais contrairement à requests
, il ne définit par défaut aucun paramètre de nom de fichier. En outre, il peut diffuser la demande à partir d'objets de fichier ouverts, où requests
construira d'abord le corps de la demande en mémoire.
Depuis que les réponses précédentes ont été écrites, les demandes ont changé. Jetez un coup d'œil au thread bug de Github pour plus de détails et à this comment pour un exemple.
En bref, le paramètre files prend une dict
, la clé étant le nom du champ de formulaire et la valeur une chaîne ou un tuple de 2, 3 ou 4 longueurs, comme décrit dans la section POST Fichier codé en plusieurs parties dans le démarrage rapide des demandes:
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-Excel', {'Expires': '0'})}
Dans ce qui précède, le tuple est composé comme suit:
(filename, data, content_type, headers)
Si la valeur est juste une chaîne, le nom du fichier sera le même que celui de la clé, comme dans ce qui suit:
>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'}
Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id"
Content-Type: application/octet-stream
72c2b6f406cdabd578c5fd7598557c52
Si la valeur est un tuple et que la première entrée est None
, la propriété filename ne sera pas incluse:
>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}
Content-Disposition: form-data; name="obvius_session_id"
Content-Type: application/octet-stream
72c2b6f406cdabd578c5fd7598557c52
Vous devez utiliser le paramètre files
pour envoyer une demande de formulaire POST en plusieurs parties voire lorsque vous n'avez pas besoin de télécharger de fichier.
De l'original requêtes source:
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.
La partie pertinente est la suivante: file-Tuple can be a
2-Tuple
, 3-Tuple
or a
4-Tuple
.
Sur la base de ce qui précède, la demande de formulaire en plusieurs parties la plus simple, qui inclut les fichiers à télécharger et les champs de formulaire, se présente comme suit:
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)
☝ Notez que None
est le premier argument du nuplet pour les champs de texte brut - il s'agit d'un espace réservé pour le champ nom de fichier utilisé uniquement pour les téléchargements de fichiers, mais pour les champs de texte avec None
comme premier paramètre requis données à soumettre.
pip install requests_toolbelt
), qui est une extension de la core). requêtes module qui prend en charge le streaming de téléchargement de fichier ainsi que le MultipartEncoder qui peut être utilisé à la place de files
et qui accepte les paramètres à la fois comme dictionnaires et tuples.MultipartEncoder
peut être utilisé à la fois pour des demandes en plusieurs parties avec ou sans champs de téléchargement réels. Il doit être affecté au paramètre 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})
Si vous devez envoyer plusieurs champs avec le même nom, ou si l'ordre des champs de formulaire est important, vous pouvez utiliser un tuple ou une liste à la place d'un dictionnaire, à savoir:
multipart_data = MultipartEncoder(
fields=(
('action', 'ingest'),
('item', 'spam'),
('item', 'sausage'),
('item', 'eggs'),
)
)
Vous devez utiliser l'attribut name
du fichier de téléchargement contenu dans le code HTML du site. Exemple:
autocomplete="off" name="image">
Vous voyez name="image">
? Vous pouvez le trouver dans le code HTML d'un site pour télécharger le fichier. Vous devez l'utiliser pour télécharger le fichier avec Multipart/form-data
scénario:
import requests
site = 'https://prnt.sc/upload.php' # the site where you upload the file
filename = 'image.jpg' # name example
Ici, à la place de l'image, ajoutez le nom du fichier de téléchargement en HTML
up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}
Si le téléchargement nécessite de cliquer sur le bouton pour le télécharger, vous pouvez utiliser comme suit:
data = {
"Button" : "Submit",
}
Puis lancez la demande
request = requests.post(site, files=up, data=data)
Et fait, le fichier a été téléchargé avec succès
Voici l'extrait de code Python dont vous avez besoin pour télécharger un seul grand fichier en tant que données de formulaire multiparties. Avec le middleware NodeJs Multer fonctionnant côté serveur.
import requests
latest_file = 'path/to/file'
url = "http://httpbin.org/apiToUpload"
files = {'fieldName': open(latest_file, 'rb')}
r = requests.put(url, files=files)
Pour le côté serveur, veuillez consulter la documentation de multer à l’adresse: https://github.com/expressjs/multer Ici, le champ single ('fieldName') est utilisé pour accepter un seul fichier, comme dans:
var upload = multer().single('fieldName');
Voici l'extrait de code simple permettant de télécharger un seul fichier avec des paramètres supplémentaires à l'aide de requêtes:
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)
Veuillez noter que vous n'avez pas besoin de spécifier explicitement un type de contenu.
NOTE: Voulait commenter une des réponses ci-dessus mais ne pouvait pas en raison de sa faible réputation, a rédigé une nouvelle réponse ici.