Скачать файл из сети в Python 3

333

Я создаю программу, которая загружает файл .jar (java) с веб-сервера, считывая URL-адрес, указанный в файле .jad той же игры / приложения. Я использую Python 3.2.1

Мне удалось извлечь URL-адрес файла JAR из файла JAD (каждый файл JAD содержит URL-адрес файла JAR), но, как вы можете себе представить, извлеченное значение является строкой type ().

Вот соответствующая функция:

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

downloadFile(URL_from_file)

Однако я всегда получаю сообщение о том, что тип в функции выше должен быть байтами, а не строкой. Я пытался использовать URL.encode ('utf-8'), а также байты (URL, encoding = 'utf-8'), но я всегда получал ту же или похожую ошибку.

Таким образом, в основном мой вопрос заключается в том, как загрузить файл с сервера, когда URL-адрес хранится в виде строки?

Бо Миланович
источник
4
@alvas, щедрость за это? Ответчик все еще (и довольно) активен на SO. Почему бы просто не добавить комментарий и спросить?
Бхаргав Рао
8
Потому что хороший ответ, который длится испытание временем, заслуживает награды. Кроме того, мы должны начать делать это для многих других вопросов, чтобы проверить, актуальны ли ответы сегодня. Особенно, когда сортировка SO ответов довольно сумасшедшая, иногда устаревший или даже худший ответ идет на вершину.
alvas

Ответы:

647

Если вы хотите получить содержимое веб-страницы в переменную, просто readответ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

Самый простой способ загрузить и сохранить файл - это использовать urllib.request.urlretrieve функцию:

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)

Но имейте в виду, что urlretrieveэто считается наследием и может устареть (хотя и не знаю почему).

Таким образом, самый правильный способ сделать это - использовать urllib.request.urlopenфункцию для возврата файла-подобного объекта, который представляет HTTP-ответ, и скопировать его в реальный файл, используя 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)

Если это кажется слишком сложным, вы можете пойти проще и сохранить всю загрузку в bytesобъекте, а затем записать его в файл. Но это хорошо работает только для небольших файлов.

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)

Можно извлекать .gz(и, возможно, другие форматы) сжатые данные на лету, но такая операция, вероятно, требует, чтобы HTTP-сервер поддерживал произвольный доступ к файлу.

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`.
Олег Припин
источник
7
Вы можете использовать response.info().get_param('charset', 'utf-8')вместо жесткого utf-8кодирования, чтобы получить кодировку символов из Content-Typeзаголовка
jfs
2
@OlehPrypin Почему outfile.write(data)хорошо работает только для небольших файлов?
Startec
"urlretrieve считается наследием и может устареть" откуда у вас эта идея?
Кори Голдберг
13
@Corey: справа от документов : «21.6.24. Устаревший интерфейс Следующие функции и классы перенесены из модуля Pyll 2 urllib (в отличие от urllib2). Они могут устареть в некоторый момент в будущем». ... и я согласен на олехе «не знаю , почему»
CFI
@ Олег Припин, если я использую с urllib.request.urlopen (url) в качестве ответа, откройте (file_name, 'wb') как out_file: shutil.copyfileobj (response, out_file), тогда как я могу найти код состояния HTTP в операторе catch узнать, что файл не найден?
Роберт Ахманн
146

я использую requests пакет всякий раз, когда хочу что-то связанное с HTTP-запросами, потому что его API очень легко начать с:

во-первых, установить requests

$ pip install requests

тогда код:

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)
Али Факи
источник
16

Надеюсь, я правильно понял вопрос: как загрузить файл с сервера, когда URL-адрес хранится в строковом типе?

Я загружаю файлы и сохраняю их локально, используя следующий код:

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()
Ранвиджай Кумар
источник
Привет, я также использую тот же тип кода для загрузки файла, но иногда я сталкиваюсь с исключением, как - кодек 'charmap' не может закодировать символ '\ u010c' ..... можете ли вы помочь мне с этим
Джойсон
10

Здесь мы можем использовать устаревший интерфейс urllib в Python3:

Следующие функции и классы портированы из urllib модуля Python 2 (в отличие от urllib2). Они могут устареть в какой-то момент в будущем.

Пример (2 строки кода) :

import urllib.request

url = 'https://www.python.org/static/img/python-logo.png'
urllib.request.urlretrieve(url, "logo.png")
Ян Юй
источник
9

Вы можете использовать wget, который является популярным инструментом для загрузки оболочки. https://pypi.python.org/pypi/wget Это будет самый простой метод, так как ему не нужно открывать файл назначения. Вот пример.

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') 
Ласит Нирошан
источник
0

Да, безусловно, запросы - это отличный пакет для использования с чем-то, связанным с HTTP-запросами. но мы должны быть осторожны с типом кодирования входящих данных, а ниже приведен пример, который объясняет разницу


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
источник
0

мотивация

Иногда мы хотим получить изображение, но не нужно загружать его в реальные файлы,

т.е., загрузить данные и сохранить его в памяти.

Например, если я использую метод машинного обучения, обучите модель, которая может распознавать изображение с номером (штрих-код).

Когда я делаю пауки на некоторых сайтах, на которых есть эти изображения, я могу использовать модель для ее распознавания,

и я не хочу сохранять эти картинки на моем диске,

тогда вы можете попробовать метод ниже, чтобы помочь вам сохранить данные загрузки в памяти.

Точки

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)

в основном, как @Ranvijay Кумар

Пример

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
источник
-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
источник