172 votes

Envoyer un fichier par POST à partir d'un script Python

Existe-t-il un moyen d'envoyer un fichier par POST à partir d'un script Python ?

250voto

Piotr Dobrogost Points 14412

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": ""
}

2 votes

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í .

1 votes

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.

20 votes

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.

55voto

Chris AtLee Points 3656

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).

31voto

John Millikin Points 86775

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!')

1 votes

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/

0 votes

L'appel à run_upload ({'server':'', 'thread':''}, paths=['/path/to/file.txt']) provoque une erreur dans cette ligne : upload_file (path) car "upload file" nécessite 3 paramètres donc je le remplace par cette ligne upload_file (path, 1, 1)

8voto

DNA Points 16180

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)

4voto

ilmarinen Points 141

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

0 votes

@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 ?

0 votes

Merci pour la réponse. En utilisant le code ci-dessus, j'ai transféré un fichier d'image brute de 2,2 Go en utilisant la requête PUT dans le serveur web.

Prograide.com

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.

Powered by:

X