Завантажте файл з Інтернету на Python 3


332

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

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

Ось відповідна функція:

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. Чому б не просто додати коментар і запитати?
Bhargav Rao

8
Тому що гарну відповідь, що триває випробування часом, варто нагородити. Крім того, ми повинні почати робити це для багатьох інших питань, щоб перевірити, чи відповіді сьогодні актуальні. Особливо, коли сортування відповідей на SO є досить божевільним, іноді застаріла або навіть найгірша відповідь йде до початку.
alvas

Відповіді:


646

Якщо ви хочете отримати вміст веб-сторінки у змінній, просто 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. Спадковий інтерфейс. Наступні функції та класи переносяться з urllib модуля Python 2 (на відміну від urllib2). Вони можуть стати застарілими в якийсь момент у майбутньому." ... і я згоден на те, що Олег "не знаю чому"
cfi

@Oleh Prypin, якщо я використовую urllib.request.urlopen (url) як відповідь, відкрити (ім'я файлу, 'wb') у вигляді файлу out_file: shutil.copyfileobj (відповідь, out_file), то як я можу знайти код статусу HTTP у виписці знати файл не знайдено?
Роберт Ахман

145

Я використовую 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' ..... чи можете ви мені допомогти з цим
Joyson

10

Тут ми можемо використовувати інтерфейс Legacy 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

Так, definetly request - це чудовий пакет, який можна використовувати в чомусь, що стосується 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)

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 Kumar

Приклад

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()

-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))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.