web-dev-qa-db-fra.com

Faire un http POST demande de téléchargement d'un fichier en utilisant python urllib/urllib2

Je souhaite effectuer une demande POST pour télécharger un fichier sur un service Web (et obtenir une réponse) à l'aide de python. Par exemple, je peux faire la demande POST suivante avec curl:

curl -F "[email protected]" -F output=json http://jigsaw.w3.org/css-validator/validator

Comment puis-je faire la même demande avec python urllib/urllib2? Le plus proche que j'ai eu jusqu'à présent est le suivant:

with open("style.css", 'r') as f:
    content = f.read()
post_data = {"file": content, "output": "json"}
request = urllib2.Request("http://jigsaw.w3.org/css-validator/validator", \
                          data=urllib.urlencode(post_data))
response = urllib2.urlopen(request)

J'ai eu une erreur HTTP 500 du code ci-dessus. Mais puisque ma commande curl réussit, il doit y avoir un problème avec ma demande python?

Je suis assez nouveau sur ce sujet et pardonnez-moi s'il vous plaît si la question de la recrue a des réponses très simples ou des erreurs. Merci d'avance pour toutes vos aides!

11
Ying Xiong

Après quelques recherches, il semble que cet article ait résolu mon problème. Il s'avère que j'ai besoin de configurer correctement l'encodeur multipart.

from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2

register_openers()

with open("style.css", 'r') as f:
    datagen, headers = multipart_encode({"file": f})
    request = urllib2.Request("http://jigsaw.w3.org/css-validator/validator", \
                              datagen, headers)
    response = urllib2.urlopen(request)
10
Ying Xiong

Personnellement, je pense que vous devriez considérer les demandes bibliothèque pour poster des fichiers.

url = 'http://jigsaw.w3.org/css-validator/validator'
files = {'file': open('style.css')}
response = requests.post(url, files=files)

Télécharger des fichiers en utilisant urllib2 n’est pas impossible, mais une tâche assez compliquée: http://pymotw.com/2/urllib2/#uploading-files

10
Wolph

Eh bien, il y a plusieurs façons de le faire. Comme mentionné ci-dessus, vous pouvez envoyer le fichier en "multipart/form-data". Cependant, le service cible peut ne pas s'attendre à ce type, auquel cas vous pouvez essayer d'autres approches.

Passer l'objet fichier

urllib2 peut accepter un objet fichier sous la forme data. Lorsque vous transmettez ce type, la bibliothèque lit le fichier en tant que flux binaire et l'envoie. Cependant, not ne définira pas le bon en-tête Content-Type. De plus, si l'en-tête Content-Length est manquant, il essaiera d'accéder à la propriété len de l'objet, qui n'existe pas pour les fichiers. Cela dit, vous devez fournir les en-têtes Content-Type et Content-Length pour que la méthode fonctionne:

import os
import urllib2

filename = '/var/tmp/myfile.Zip'
headers = {
    'Content-Type': 'application/Zip',
    'Content-Length': os.stat(filename).st_size,
}
request = urllib2.Request('http://localhost', open(filename, 'rb'),
                          headers=headers)
response = urllib2.urlopen(request)

Enveloppe l'objet fichier

Pour ne pas traiter la longueur, vous pouvez créer un objet wrapper simple. Avec juste un peu de changement, vous pouvez l'adapter pour obtenir le contenu d'une chaîne si le fichier est chargé en mémoire.

class BinaryFileObject:
  """Simple wrapper for a binary file for urllib2."""

  def __init__(self, filename):
    self.__size = int(os.stat(filename).st_size)
    self.__f = open(filename, 'rb')

  def read(self, blocksize):
    return self.__f.read(blocksize)

  def __len__(self):
    return self.__size

Encoder le contenu en base64

Une autre méthode consiste à coder data via base64.b64encode et à fournir un en-tête Content-Transfer-Type: base64. Cependant, cette méthode nécessite un support côté serveur. Selon l'implémentation, le service peut accepter le fichier et le stocker de manière incorrecte ou renvoyer HTTP 400. Par exemple. l'API GitHub ne générera pas d'erreur, mais le fichier téléchargé sera corrompu.

1
real4x