Existe-t-il un moyen d'envoyer un fichier par POST à partir d'un script Python ?
J'essaie de faire la même chose et cela fonctionne bien si la taille du fichier est inférieure à ~1.5 MB. Sinon, une erreur est signalée. aquí .
Existe-t-il un moyen d'envoyer un fichier par POST à partir d'un script Python ?
De : https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file
Requests permet de télécharger très simplement des fichiers codés en Multipart :
with open('report.xls', 'rb') as f:
r = requests.post('http://httpbin.org/post', files={'report.xls': f})
C'est ça. Je ne plaisante pas - c'est une ligne de code. Le fichier a été envoyé. Vérifions :
>>> r.text
{
"origin": "179.13.100.4",
"files": {
"report.xls": "<censored...binary...data>"
},
"form": {},
"url": "http://httpbin.org/post",
"args": {},
"headers": {
"Content-Length": "3196",
"Accept-Encoding": "identity, deflate, compress, gzip",
"Accept": "*/*",
"User-Agent": "python-requests/0.8.0",
"Host": "httpbin.org:80",
"Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"
},
"data": ""
}
J'essaie de faire la même chose et cela fonctionne bien si la taille du fichier est inférieure à ~1.5 MB. Sinon, une erreur est signalée. aquí .
Ce que j'essaie de faire est de me connecter à un site en utilisant une requête, ce que j'ai fait avec succès, mais maintenant je veux télécharger une vidéo après m'être connecté et le formulaire a différents champs à remplir avant la soumission. Alors, comment dois-je transmettre ces valeurs comme la description de la vidéo, le titre de la vidéo, etc.
Vous voudriez probablement faire with open('report.xls', 'rb') as f: r = requests.post('http://httpbin.org/post', files={'report.xls': f})
à la place, de sorte qu'il ferme à nouveau le fichier après l'avoir ouvert.
Autopromotion flagrante :
consultez mon poster pour python. Il gère l'encodage multipart/form-data et prend en charge les téléchargements en continu (afin de ne pas avoir à charger le fichier entier en mémoire avant de soumettre la requête HTTP POST).
Oui. Vous utiliseriez le urllib2
et coder en utilisant le module multipart/form-data
type de contenu. Voici un exemple de code pour vous aider à démarrer - il s'agit d'un peu plus que du simple téléchargement de fichiers, mais vous devriez pouvoir le lire et voir comment il fonctionne :
user_agent = "image uploader"
default_message = "Image $current of $total"
import logging
import os
from os.path import abspath, isabs, isdir, isfile, join
import random
import string
import sys
import mimetypes
import urllib2
import httplib
import time
import re
def random_string (length):
return ''.join (random.choice (string.letters) for ii in range (length + 1))
def encode_multipart_data (data, files):
boundary = random_string (30)
def get_content_type (filename):
return mimetypes.guess_type (filename)[0] or 'application/octet-stream'
def encode_field (field_name):
return ('--' + boundary,
'Content-Disposition: form-data; name="%s"' % field_name,
'', str (data [field_name]))
def encode_file (field_name):
filename = files [field_name]
return ('--' + boundary,
'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename),
'Content-Type: %s' % get_content_type(filename),
'', open (filename, 'rb').read ())
lines = []
for name in data:
lines.extend (encode_field (name))
for name in files:
lines.extend (encode_file (name))
lines.extend (('--%s--' % boundary, ''))
body = '\r\n'.join (lines)
headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
'content-length': str (len (body))}
return body, headers
def send_post (url, data, files):
req = urllib2.Request (url)
connection = httplib.HTTPConnection (req.get_host ())
connection.request ('POST', req.get_selector (),
*encode_multipart_data (data, files))
response = connection.getresponse ()
logging.debug ('response = %s', response.read ())
logging.debug ('Code: %s %s', response.status, response.reason)
def make_upload_file (server, thread, delay = 15, message = None,
username = None, email = None, password = None):
delay = max (int (delay or '0'), 15)
def upload_file (path, current, total):
assert isabs (path)
assert isfile (path)
logging.debug ('Uploading %r to %r', path, server)
message_template = string.Template (message or default_message)
data = {'MAX_FILE_SIZE': '3145728',
'sub': '',
'mode': 'regist',
'com': message_template.safe_substitute (current = current, total = total),
'resto': thread,
'name': username or '',
'email': email or '',
'pwd': password or random_string (20),}
files = {'upfile': path}
send_post (server, data, files)
logging.info ('Uploaded %r', path)
rand_delay = random.randint (delay, delay + 5)
logging.debug ('Sleeping for %.2f seconds------------------------------\n\n', rand_delay)
time.sleep (rand_delay)
return upload_file
def upload_directory (path, upload_file):
assert isabs (path)
assert isdir (path)
matching_filenames = []
file_matcher = re.compile (r'\.(?:jpe?g|gif|png)$', re.IGNORECASE)
for dirpath, dirnames, filenames in os.walk (path):
for name in filenames:
file_path = join (dirpath, name)
logging.debug ('Testing file_path %r', file_path)
if file_matcher.search (file_path):
matching_filenames.append (file_path)
else:
logging.info ('Ignoring non-image file %r', path)
total_count = len (matching_filenames)
for index, file_path in enumerate (matching_filenames):
upload_file (file_path, index + 1, total_count)
def run_upload (options, paths):
upload_file = make_upload_file (**options)
for arg in paths:
path = abspath (arg)
if isdir (path):
upload_directory (path, upload_file)
elif isfile (path):
upload_file (path)
else:
logging.error ('No such path: %r' % path)
logging.info ('Done!')
Sous python 2.6.6, j'ai obtenu une erreur dans l'analyse des frontières multipartites en utilisant ce code sous Windows. J'ai dû passer de string.letters à string.ascii_letters comme indiqué à l'adresse suivante stackoverflow.com/questions/2823316/ pour que cela fonctionne. L'exigence relative aux limites est abordée ici : stackoverflow.com/questions/147451/
Voir également le Demandes ("HTTP for Humans"), qui prend en charge divers services de Saveurs de POST dont POST multi-parties d'un fichier :
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)
La seule chose qui vous empêche d'utiliser urlopen directement sur un objet fichier est le fait que l'objet fichier intégré n'a pas d'objet de type len définition. Une façon simple est de créer une sous-classe, qui fournit à urlopen le bon fichier. J'ai également modifié l'en-tête Content-Type dans le fichier ci-dessous.
import os
import urllib2
class EnhancedFile(file):
def __init__(self, *args, **keyws):
file.__init__(self, *args, **keyws)
def __len__(self):
return int(os.fstat(self.fileno())[6])
theFile = EnhancedFile('a.xml', 'r')
theUrl = "http://example.com/abcde"
theHeaders= {'Content-Type': 'text/xml'}
theRequest = urllib2.Request(theUrl, theFile, theHeaders)
response = urllib2.urlopen(theRequest)
theFile.close()
for line in response:
print line
@robert J'ai testé votre code en Python2.7 mais il ne fonctionne pas. urlopen(Request(theUrl, theFile, ...)) encode simplement le contenu du fichier comme s'il s'agissait d'un post normal mais ne peut pas spécifier le champ de formulaire correct. J'ai même essayé la variante urlopen(theUrl, urlencode({'serverside_field_name' : EnhancedFile('mon_fichier.txt')})), il télécharge un fichier mais (bien sûr !) avec un contenu incorrect comme <ouvert le fichier 'mon_fichier.txt', mode 'r' à 0x00D6B718>. Est-ce que j'ai raté quelque chose ?
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.