web-dev-qa-db-fra.com

urllib.urlretrieve avec en-tête personnalisé

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?

10
realUser404

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 =

38
Lost Crotchet

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:

  1. Crée à partir de l'objet URL urllib.request.Request (Ou si vous fournissez directement le Request il l'utilisera simplement).
  2. De l'objet Request est extrait le protocole (il déduit du schéma URL).
  3. Basé sur le protocole, il essaiera la recherche et utilisera les méthodes suivantes:
    • 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 distant
    • protocol_response - traite la réponse du serveur
    • pour d'autres méthodes, regardez la documentation de Python

Pour votre propre ouvreur, vous devez suivre ces 3 étapes:

  1. Créer son propre gestionnaire
  2. La liste de construction des gestionnaires contient votre gestionnaire personnalisé (fonction urllib.request.build_opener)
  3. Installez le nouvel ouvreur dans 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.

6
Qeek