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