Télécharger le fichier à partir du Web en Python 3

333

Je crée un programme qui téléchargera un fichier .jar (java) à partir d'un serveur Web, en lisant l'URL spécifiée dans le fichier .jad du même jeu / application. J'utilise Python 3.2.1

J'ai réussi à extraire l'URL du fichier JAR du fichier JAD (chaque fichier JAD contient l'URL du fichier JAR), mais comme vous pouvez l'imaginer, la valeur extraite est la chaîne type ().

Voici la fonction pertinente:

def downloadFile(URL=None):
    import httplib2
    h = httplib2.Http(".cache")
    resp, content = h.request(URL, "GET")
    return content

downloadFile(URL_from_file)

Cependant, j'obtiens toujours une erreur en disant que le type dans la fonction ci-dessus doit être des octets et non une chaîne. J'ai essayé d'utiliser l'URL.encode ('utf-8'), ainsi que les octets (URL, encoding = 'utf-8'), mais j'obtiendrais toujours la même erreur ou une erreur similaire.

Donc, fondamentalement, ma question est de savoir comment télécharger un fichier à partir d'un serveur lorsque l'URL est stockée dans un type de chaîne?

Bo Milanovich
la source
4
@alvas, une prime pour cela? Le répondeur est toujours (et assez) actif sur SO. Pourquoi ne pas simplement ajouter un commentaire et demander?
Bhargav Rao
8
Cos une bonne réponse qui dure l'épreuve du temps mérite d'être récompensée. En outre, nous devrions commencer à le faire pour de nombreuses autres questions afin de vérifier si les réponses sont pertinentes aujourd'hui. Surtout lorsque le tri des réponses SO est assez fou, parfois la réponse obsolète ou même la pire va au sommet.
alvas

Réponses:

647

Si vous voulez obtenir le contenu d'une page Web dans une variable, juste readla réponse de urllib.request.urlopen:

import urllib.request
...
url = 'http://example.com/'
response = urllib.request.urlopen(url)
data = response.read()      # a `bytes` object
text = data.decode('utf-8') # a `str`; this step can't be used if data is binary

La façon la plus simple de télécharger et d'enregistrer un fichier est d'utiliser la urllib.request.urlretrievefonction:

import urllib.request
...
# Download the file from `url` and save it locally under `file_name`:
urllib.request.urlretrieve(url, file_name)
import urllib.request
...
# Download the file from `url`, save it in a temporary directory and get the
# path to it (e.g. '/tmp/tmpb48zma.txt') in the `file_name` variable:
file_name, headers = urllib.request.urlretrieve(url)

Mais gardez à l'esprit que cela urlretrieveest considéré comme un héritage et pourrait devenir obsolète (je ne sais pas pourquoi, cependant).

Donc, la façon la plus correcte de le faire serait d'utiliser la urllib.request.urlopenfonction pour renvoyer un objet de type fichier qui représente une réponse HTTP et de le copier dans un fichier réel à l'aide de shutil.copyfileobj.

import urllib.request
import shutil
...
# Download the file from `url` and save it locally under `file_name`:
with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file:
    shutil.copyfileobj(response, out_file)

Si cela semble trop compliqué, vous voudrez peut-être aller plus simple et stocker tout le téléchargement dans un bytesobjet, puis l'écrire dans un fichier. Mais cela ne fonctionne bien que pour les petits fichiers.

import urllib.request
...
# Download the file from `url` and save it locally under `file_name`:
with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file:
    data = response.read() # a `bytes` object
    out_file.write(data)

Il est possible d'extraire .gz(et peut-être d'autres formats) des données compressées à la volée, mais une telle opération nécessite probablement le serveur HTTP pour prendre en charge l'accès aléatoire au fichier.

import urllib.request
import gzip
...
# Read the first 64 bytes of the file inside the .gz archive located at `url`
url = 'http://example.com/something.gz'
with urllib.request.urlopen(url) as response:
    with gzip.GzipFile(fileobj=response) as uncompressed:
        file_header = uncompressed.read(64) # a `bytes` object
        # Or do anything shown above using `uncompressed` instead of `response`.
Oleh Prypin
la source
7
vous pouvez utiliser à la response.info().get_param('charset', 'utf-8')place du codage en dur utf-8, pour obtenir l'encodage des caractères à partir de l'en- Content-Typetête
jfs
2
@OlehPrypin Pourquoi ne outfile.write(data)fonctionne bien que pour les petits fichiers?
Startec
"urlretrieve est considéré comme hérité et pourrait devenir obsolète" d'où vous est venue cette idée?
Corey Goldberg
13
@Corey: Directement à partir de la documentation : "21.6.24. Interface héritée Les fonctions et classes suivantes sont portées depuis le module Python 2 urllib (par opposition à urllib2). Elles pourraient devenir obsolètes à un moment donné dans le futur." ... et je suis d'accord sur le "pas sûr pourquoi" d'
Oleh
@Oleh Prypin si j'utilise avec urllib.request.urlopen (url) comme réponse, ouvrez (nom_fichier, 'wb') en tant que fichier_out: shutil.copyfileobj (réponse, fichier_out), puis comment puis-je trouver le code d'état HTTP dans l'instruction catch savoir que le fichier n'a pas été trouvé?
Robert Achmann
146

J'utilise un requestspackage chaque fois que je veux quelque chose en rapport avec les requêtes HTTP car son API est très facile à démarrer:

installez d'abord requests

$ pip install requests

puis le code:

from requests import get  # to make GET request


def download(url, file_name):
    # open in binary mode
    with open(file_name, "wb") as file:
        # get request
        response = get(url)
        # write to file
        file.write(response.content)
Ali Faki
la source
16

J'espère avoir bien compris la question, qui est: comment télécharger un fichier à partir d'un serveur lorsque l'URL est stockée dans un type de chaîne?

Je télécharge des fichiers et les enregistre localement en utilisant le code ci-dessous:

import requests

url = 'https://www.python.org/static/img/python-logo.png'
fileName = 'D:\Python\dwnldPythonLogo.png'
req = requests.get(url)
file = open(fileName, 'wb')
for chunk in req.iter_content(100000):
    file.write(chunk)
file.close()
Ranvijay Kumar
la source
salut, j'utilise également le même type de code pour télécharger le fichier, mais j'ai parfois du mal à faire face à une exception comme - le codec 'charmap' ne peut pas coder le caractère '\ u010c' ..... pouvez-vous m'aider avec cela
Joyson
10

Ici, nous pouvons utiliser l'interface Legacy d'urllib en Python3:

Les fonctions et classes suivantes sont portées depuis le module Python 2 urllib (par opposition à urllib2). Ils pourraient devenir obsolètes à un moment donné dans le futur.

Exemple (code 2 lignes) :

import urllib.request

url = 'https://www.python.org/static/img/python-logo.png'
urllib.request.urlretrieve(url, "logo.png")
Yang Yu
la source
9

Vous pouvez utiliser wget qui est un outil de téléchargement populaire pour cela. https://pypi.python.org/pypi/wget Ce sera la méthode la plus simple car elle n'a pas besoin d'ouvrir le fichier de destination. Voici un exemple.

import wget
url = 'https://i1.wp.com/python3.codes/wp-content/uploads/2015/06/Python3-powered.png?fit=650%2C350'  
wget.download(url, '/Users/scott/Downloads/cat4.jpg') 
Lasith Niroshan
la source
0

Oui, les requêtes sont définitivement un excellent package à utiliser dans quelque chose en rapport avec les requêtes HTTP. mais nous devons être prudents avec le type d'encodage des données entrantes ainsi que ci-dessous est un exemple qui explique la différence


from requests import get

# case when the response is byte array
url = 'some_image_url'

response = get(url)
with open('output', 'wb') as file:
    file.write(response.content)


# case when the response is text
# Here unlikely if the reponse content is of type **iso-8859-1** we will have to override the response encoding
url = 'some_page_url'

response = get(url)
# override encoding by real educated guess as provided by chardet
r.encoding = r.apparent_encoding

with open('output', 'w', encoding='utf-8') as file:
    file.write(response.content)
Kaushal
la source
0

Motivation

Parfois, nous voulons obtenir l'image mais pas besoin de la télécharger dans de vrais fichiers,

c'est-à-dire, téléchargez les données et conservez-les en mémoire.

Par exemple, si j'utilise la méthode d'apprentissage automatique, entraînez un modèle capable de reconnaître une image avec le numéro (code à barres).

Lorsque j'explore certains sites Web qui contiennent ces images afin que je puisse utiliser le modèle pour le reconnaître,

et je ne veux pas enregistrer ces images sur mon lecteur de disque,

alors vous pouvez essayer la méthode ci-dessous pour vous aider à conserver les données de téléchargement en mémoire.

Points

import requests
from io import BytesIO
response = requests.get(url)
with BytesIO as io_obj:
    for chunk in response.iter_content(chunk_size=4096):
        io_obj.write(chunk)

en gros, c'est comme @Ranvijay Kumar

Un exemple

import requests
from typing import NewType, TypeVar
from io import StringIO, BytesIO
import matplotlib.pyplot as plt
import imageio

URL = NewType('URL', str)
T_IO = TypeVar('T_IO', StringIO, BytesIO)


def download_and_keep_on_memory(url: URL, headers=None, timeout=None, **option) -> T_IO:
    chunk_size = option.get('chunk_size', 4096)  # default 4KB
    max_size = 1024 ** 2 * option.get('max_size', -1)  # MB, default will ignore.
    response = requests.get(url, headers=headers, timeout=timeout)
    if response.status_code != 200:
        raise requests.ConnectionError(f'{response.status_code}')

    instance_io = StringIO if isinstance(next(response.iter_content(chunk_size=1)), str) else BytesIO
    io_obj = instance_io()
    cur_size = 0
    for chunk in response.iter_content(chunk_size=chunk_size):
        cur_size += chunk_size
        if 0 < max_size < cur_size:
            break
        io_obj.write(chunk)
    io_obj.seek(0)
    """ save it to real file.
    with open('temp.png', mode='wb') as out_f:
        out_f.write(io_obj.read())
    """
    return io_obj


def main():
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Host': 'statics.591.com.tw',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'
    }
    io_img = download_and_keep_on_memory(URL('http://statics.591.com.tw/tools/showPhone.php?info_data=rLsGZe4U%2FbphHOimi2PT%2FhxTPqI&type=rLEFMu4XrrpgEw'),
                                         headers,  # You may need this. Otherwise, some websites will send the 404 error to you.
                                         max_size=4)  # max loading < 4MB
    with io_img:
        plt.rc('axes.spines', top=False, bottom=False, left=False, right=False)
        plt.rc(('xtick', 'ytick'), color=(1, 1, 1, 0))  # same of plt.axis('off')
        plt.imshow(imageio.imread(io_img, as_gray=False, pilmode="RGB"))
        plt.show()


if __name__ == '__main__':
    main()
Carson
la source
-3
from urllib import request

def get(url):
    with request.urlopen(url) as r:
        return r.read()


def download(url, file=None):
    if not file:
        file = url.split('/')[-1]
    with open(file, 'wb') as f:
        f.write(get(url))
user7726287
la source