Как скачать файл с urllib3?

Это основано на другом вопросе на этом сайте: Как лучше всего для загрузки файла с помощью urllib3 Однако я не могу комментировать там, поэтому задаю другой вопрос:

Как загрузить файл (большего размера) с помощью urllib3?

Я попытался использовать тот же код, который работает с urllib2 (Загрузить файл из Интернета в Python 3), но с urllib3 не получается:

http = urllib3.PoolManager()

with http.request('GET', url) as r, open(path, 'wb') as out_file:       
    #shutil.copyfileobj(r.data, out_file) # this writes a zero file
    shutil.copyfileobj(r.data, out_file)

Это говорит о том, что объект 'bytes' не имеет атрибута 'read'

Затем я попытался использовать код в этом вопросе, но он застрял в бесконечном цикле, потому что данные всегда равны «0»:

http = urllib3.PoolManager()
r = http.request('GET', url)

with open(path, 'wb') as out:
    while True:
        data = r.read(4096)         
        if data is None:
            break
        out.write(data)
r.release_conn()

Однако, если я читаю все в памяти, файл загружается правильно:

http = urllib3.PoolManager()
r = http.request('GET', url)
with open(path, 'wb') as out:
    out.write(data)

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

(Кроме того, пожалуйста, не предлагайте запросы или urllib2, потому что они недостаточно гибки, когда речь идет о самозаверяющих сертификатах.)


person Alecz    schedule 09.12.2014    source источник


Ответы (1)


Вы были очень близки, недостающая часть - это установка preload_content=False (это будет значение по умолчанию в следующей версии). Также вы можете рассматривать ответ как файлоподобный объект, а не как атрибут .data (это волшебное свойство, которое, надеюсь, когда-нибудь устареет).

- with http.request('GET', url) ...
+ with http.request('GET', url, preload_content=False) ...

Этот код должен работать:

http = urllib3.PoolManager()

with http.request('GET', url, preload_content=False) as r, open(path, 'wb') as out_file:       
    shutil.copyfileobj(r, out_file)

Объект ответа urllib3 также учитывает интерфейс io, поэтому вы также можете делать такие вещи, как. ..

import io
response = http.request(..., preload_content=False)
buffered_response = io.BufferedReader(response, 2048)

Пока вы добавляете preload_content=False к любой из ваших трех попыток и обрабатываете ответ как файловый объект, все они должны работать.

К сожалению, документация urllib не охватывает передовой опыт в этой теме.

Вы совершенно правы, я надеюсь, вы подумаете о том, чтобы помочь нам задокументировать этот вариант использования, отправив запрос на включение сюда: https://github.com/shazow/urllib3

person shazow    schedule 09.12.2014
comment
Спасибо, это сработало! preload_content не так хорошо задокументирован. - person Alecz; 10.12.2014