J'essaie de récupérer un fichier en utilisant urlretrieve
, tout en ajoutant un en-tête personnalisé.
Lors de la vérification de la source de code de urllib.request
J'ai réalisé que urlopen
peut prendre un objet Request
en paramètre au lieu d'une simple chaîne, permettant de mettre l'en-tête que je veux. Mais si j'essaie de faire la même chose avec urlretrieve
, j'obtiens un TypeError: chaîne attendue ou objet semblable à des octets comme mentionné dans cet autre article.
Ce que j'ai fini par réécrire ma propre urlretrieve, en supprimant la ligne lançant l'erreur (cette ligne n'est pas pertinente dans mon cas d'utilisation).
Ça marche bien mais je me demande s'il y a un meilleur/plus propre plutôt que de réécrire ma propre urlretrieve
. S'il est possible de passer un en-tête personnalisé à urlopen
, il semble qu'il devrait être possible de faire de même avec urlretrieve
?
J'ai trouvé un moyen où vous n'avez qu'à ajouter quelques lignes de code supplémentaires ...
import urllib.request
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
urllib.request.urlretrieve("type URL here", "path/file_name")
Si vous souhaitez en savoir plus sur les détails, vous pouvez vous référer à la documentation python: https://docs.python.org/3/library/urllib.request.html =
L'utilisation de urllib.request.urlretrieve()
à l'intérieur de urllib.request.urlopen()
(au moins dans Python 3). Vous pouvez donc utiliser de la même manière comment vous pouvez influencer le comportement de urlopen
.
Lorsque urlopen(params)
est invoqué, il regarde en fait d'abord la variable globale spéciale urllib.request._opener
Et s'il s'agit de None
, alors le urlopen
définit la variable avec l'ensemble d'ouvreurs par défaut sinon il le gardera tel quel. Dans l'étape suivante, il appellera urllib.request._opener.open(<urlopen_params>)
(dans les sections suivantes, je ne ferai référence à urllib.request._opener
Que opener
).
Le opener.open()
contient une liste de gestionnaires pour différents protocoles. Lorsque opener.open()
est appelé, il effectuera les actions suivantes:
urllib.request.Request
(Ou si vous fournissez directement le Request
il l'utilisera simplement).Request
est extrait le protocole (il déduit du schéma URL).protocol_request
(Par exemple http_request
) - utilisé pour prétraiter la demande avant l'ouverture de la connexion.protocol_open
- crée réellement une connexion avec le serveur distantprotocol_response
- traite la réponse du serveurPour votre propre ouvreur, vous devez suivre ces 3 étapes:
urllib.request.build_opener
)urllib.request._opener
(Fonction urllib.request.install_opener
)Le urllib.request.build_opener
Crée un ouvreur qui contient votre gestionnaire personnalisé et ajoute des ouvreurs par défaut à l'exception des gestionnaires dont votre gestionnaire personnalisé est hérité.
Donc, pour ajouter un en-tête personnalisé, vous pouvez écrire quelque chose comme ceci:
import urllib.request as req
class MyHTTP(req.HTTPHandler):
def http_request(self, req):
req.headers["MyHeader"] = "Content of my header"
return super().http_request(req)
opener = req.build_opener(MyHTTP())
req.install_opener(opener)
À partir de ce moment, lorsque vous appelez urllib.request.urlretrieve()
ou tout ce qui utilise le urlopen()
, il utilisera pour la communication HTTP votre gestionnaire. Lorsque vous souhaitez revenir aux gestionnaires par défaut, vous pouvez simplement appeler:
import urllib.request as req
req.install_opener(req.build_opener())
Pour être honnête, je ne sais pas si c'est une solution meilleure/plus propre que la vôtre, mais elle utilise des mécanismes préparés dans le urllib
.