Как скачать изображение с помощью запросов


369

Я пытаюсь загрузить и сохранить изображение из Интернета с помощью requestsмодуля Python .

Вот (рабочий) код, который я использовал:

img = urllib2.urlopen(settings.STATICMAP_URL.format(**data))
with open(path, 'w') as f:
    f.write(img.read())

Вот новый (нерабочий) код, использующий requests:

r = requests.get(settings.STATICMAP_URL.format(**data))
if r.status_code == 200:
    img = r.raw.read()
    with open(path, 'w') as f:
        f.write(img)

Можете ли вы помочь мне с каким атрибутом из ответа использовать requests?


16
чтобы использовать r.raw вам нужно установить stream = True
— clsung

Отвечает ли это на ваш вопрос? Скачать большой файл в python с запросами
— AMC

Ответы:


517

Вы можете использовать response.rawобъект файла или выполнить итерацию ответа.

Использование response.rawфайлового объекта по умолчанию не будет декодировать сжатые ответы (с помощью GZIP или deflate). В любом случае вы можете принудительно распаковать его, установив для decode_contentатрибута значение True( requestsзадает Falseдля управления самим декодированием). Затем вы можете использовать shutil.copyfileobj()Python для потоковой передачи данных в файловый объект:

import requests
import shutil

r = requests.get(settings.STATICMAP_URL.format(**data), stream=True)
if r.status_code == 200:
    with open(path, 'wb') as f:
        r.raw.decode_content = True
        shutil.copyfileobj(r.raw, f)        

Для перебора ответа используйте цикл; итерация, подобная этой, гарантирует, что данные распаковываются на этом этапе:

r = requests.get(settings.STATICMAP_URL.format(**data), stream=True)
if r.status_code == 200:
    with open(path, 'wb') as f:
        for chunk in r:
            f.write(chunk)

Это прочитает данные в 128 байтных чанках; если вы чувствуете, что другой размер чанка работает лучше, используйте Response.iter_content()метод с пользовательским размером чанка:

r = requests.get(settings.STATICMAP_URL.format(**data), stream=True)
if r.status_code == 200:
    with open(path, 'wb') as f:
        for chunk in r.iter_content(1024):
            f.write(chunk)

Обратите внимание, что вам нужно открыть файл назначения в двоичном режиме, чтобы python не пытался переводить переводы строк для вас. Мы также настроили stream=Trueтак, чтобы requestsсначала не загружать все изображение в память.


2
С помощью вашего ответа я смог найти данные в текстовом файле, шаги, которые я использовал r2 = requests.post(r.url, data); print r2.content. Но теперь я тоже хочу знать filename. их какой-нибудь очищенный способ? - в настоящее время я нашел имя файла в заголовке - r2.headers['content-disposition'] это дает мне вывод как: 'attachment; filename=DELS36532G290115.csi' я анализирую эту строку для имени файла ... их какой-либо более чистый способ?
— Грижеш Чаухан

6
@GrijeshChauhan: да, content-dispositionзаголовок это путь сюда; использовать cgi.parse_header()для его анализа и получения параметров; params = cgi.parse_header(r2.headers['content-disposition'])[1]тогда params['filename'].
— Мартин Питерс

1
Чтобы получить по умолчанию 128 байт ломти с, вам нужно перебрать на requests.Responseсебя : for chunk in r: .... Вызов iter_content()без операцииchunk_size будет повторяться в 1-байтовых чанках .
— 23: 23


1
@KumZ две причины: response.okникогда не был задокументирован, и он дает истину для любого статуса 1xx, 2xx или 3xx, но только ответ 200 имеет тело ответа.
— Мартин Питерс

232

Получите объектоподобный объект из запроса и скопируйте его в файл. Это также позволит избежать одновременного считывания всего этого в память.

import shutil

import requests

url = 'http://example.com/img.png'
response = requests.get(url, stream=True)
with open('img.png', 'wb') as out_file:
    shutil.copyfileobj(response.raw, out_file)
del response

14
Большое спасибо за то, что вернулись и ответили на это. Хотя другой ответ работает, на этот раз все проще
— dkroy

11
Стоит отметить, что немногие серверы настроили GZIP для своих изображений, потому что изображения уже имеют свою собственную компрессию. Это контрпродуктивно, тратит впустую циклы процессора с небольшой выгодой. Так что, хотя это может быть проблемой с текстовым контентом, особенно с изображениями, это не так.
— phette23

3
есть ли способ получить доступ к исходному имени файла
— mahes

@ phette23 Также стоит отметить, что Google PageSpeed ​​сообщает и делает это по умолчанию.
— Вечерняя ночь

8
Следует установить r.raw.decode_content = Trueраньше, shutil.copyfileobj(response.raw, out_file)потому что by default, decode compressed responses (with GZIP or deflate), так что вы получите изображение с нулевым файлом.
— Симин Цзе

167

Как насчет этого, быстрое решение.

import requests

url = "http://craphound.com/images/1006884_2adf8fc7.jpg"
response = requests.get(url)
if response.status_code == 200:
    with open("/Users/apple/Desktop/sample.jpg", 'wb') as f:
        f.write(response.content)

1
что ты имеешь в виду! f = open("/Users/apple/Desktop/sample.jpg", 'wb')что ты имеешь ввиду под этим путем ?! Я хочу скачать изображение
— улыбка

3
Это открывает дескриптор файла по указанному пути, в который можно записать файл изображения.
— Киранбкришна

@AndrewGlazkov Я думаю, что это было бы более Python для использованияif response.ok:
— EndermanAPM

5
response.ok имеет значение True для любого статуса 1xx, 2xx или 3xx, но только ответ 200 имеет тело ответа в виде @Martijn Pieters, упомянутого в комментариях выше
— annndrey

75

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

Затем я попробовал способ, рекомендованный автором модуля запросов:

import requests
from PIL import Image
# python2.x, use this instead  
# from StringIO import StringIO
# for python3.x,
from io import StringIO

r = requests.get('https://example.com/image.jpg')
i = Image.open(StringIO(r.content))

Это значительно уменьшило количество вызовов функций и ускорило работу моего приложения. Вот код моего профилировщика и результат.

#!/usr/bin/python
import requests
from StringIO import StringIO
from PIL import Image
import profile

def testRequest():
    image_name = 'test1.jpg'
    url = 'http://example.com/image.jpg'

    r = requests.get(url, stream=True)
    with open(image_name, 'wb') as f:
        for chunk in r.iter_content():
            f.write(chunk)

def testRequest2():
    image_name = 'test2.jpg'
    url = 'http://example.com/image.jpg'

    r = requests.get(url)

    i = Image.open(StringIO(r.content))
    i.save(image_name)

if __name__ == '__main__':
    profile.run('testUrllib()')
    profile.run('testUrllib2()')
    profile.run('testRequest()')

Результат для testRequest:

343080 function calls (343068 primitive calls) in 2.580 seconds

И результат для testRequest2:

3129 function calls (3105 primitive calls) in 0.024 seconds

13
Это потому, что вы не указали chunk_sizeпараметр, который по умолчанию iter_contentравен 1, поэтому выполняется итерация по потоку результатов по 1 байту за раз. Смотрите документацию python-requests.org/en/latest/api/… .
— CadentOrange

10
Это также загружает весь ответ в память, чего вы можете избежать. Здесь PILтоже нельзя использовать , with open(image_name, 'wb') as outfile: outfile.write(r.content)достаточно просто .
— Мартин Питерс

3
PILтакже отсутствует в стандартной библиотеке, что делает его немного менее переносимым.
— JJJ

2
@ZhenyiZhang iter_contentмедленный, потому что ваш chunk_sizeслишком мал, если вы увеличите его до 100k, это будет намного быстрее.
— Ван

Это лучший ответ. Не всегда лучше читать файл в память, но OP указывает «изображения», то есть размер файлов обычно не превышает 4 МБ, что оказывает тривиальное влияние на память.
— Крис Конлан

52

Это может быть проще, чем использовать requests. Это единственный раз, когда я советую не использовать requestsHTTP-вещи.

Два лайнера с использованием urllib:

>>> import urllib
>>> urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")

Есть также хороший модуль с именем Python, wgetкоторый довольно прост в использовании. Нашел здесь .

Это демонстрирует простоту конструкции:

>>> import wget
>>> url = 'http://www.futurecrew.com/skaven/song_files/mp3/razorback.mp3'
>>> filename = wget.download(url)
100% [................................................] 3841532 / 3841532>
>> filename
'razorback.mp3'

Наслаждаться.

Редактировать: Вы также можете добавить outпараметр, чтобы указать путь.

>>> out_filepath = <output_filepath>    
>>> filename = wget.download(url, out=out_filepath)

Я использовал wgetбез каких-либо неприятностей. Спасибо за указание преимуществ использованияurllib3
— h3xh4wk

1
Обратите внимание, что этот ответ для Python 2. Для Python 3 вам нужно сделать urllib.request.urlretrieve("http://example.com", "file.ext").
— Хаски

1
Спасибо @ Хаски. Обновлено.
— Blairg23

28

Следующий фрагмент кода загружает файл.

Файл сохраняется с именем файла, как указано в URL.

import requests

url = "http://example.com/image.jpg"
filename = url.split("/")[-1]
r = requests.get(url, timeout=0.5)

if r.status_code == 200:
    with open(filename, 'wb') as f:
        f.write(r.content)

16

Есть 2 основных способа:

  1. Используя .content( самый простой / официальный) (см. Ответ Женьи Чжана ):

    import io  # Note: io.BytesIO is StringIO.StringIO on Python2.
    import requests
    
    r = requests.get('http://lorempixel.com/400/200')
    r.raise_for_status()
    with io.BytesIO(r.content) as f:
        with Image.open(f) as img:
            img.show()
  2. Используя .raw(см . Ответ Мартин Питерс ):

    import requests
    
    r = requests.get('http://lorempixel.com/400/200', stream=True)
    r.raise_for_status()
    r.raw.decode_content = True  # Required to decompress gzip/deflate compressed responses.
    with PIL.Image.open(r.raw) as img:
        img.show()
    r.close()  # Safety when stream=True ensure the connection is released.

Сроки оба не показывают заметной разницы.


2
Я попробовал кучу ответов, и ваш 1.ответ (с использованием io.BytesIOи Image) был первым, который работал для меня на Python 3.6. Не забывайте from PIL import Image(и pip install Pillow).
— Коллин

Чем отличается .content от .raw?
— foxiris

13

Так же просто, как импортировать изображения и запросы

from PIL import Image
import requests

img = Image.open(requests.get(url, stream = True).raw)
img.save('img1.jpg')

4

Вот более удобный ответ, который все еще использует потоковую передачу.

Просто определите эти функции и вызовите getImage(). Он будет использовать то же имя файла, что и URL, и будет записывать в текущий каталог по умолчанию, но оба они могут быть изменены.

import requests
from StringIO import StringIO
from PIL import Image

def createFilename(url, name, folder):
    dotSplit = url.split('.')
    if name == None:
        # use the same as the url
        slashSplit = dotSplit[-2].split('/')
        name = slashSplit[-1]
    ext = dotSplit[-1]
    file = '{}{}.{}'.format(folder, name, ext)
    return file

def getImage(url, name=None, folder='./'):
    file = createFilename(url, name, folder)
    with open(file, 'wb') as f:
        r = requests.get(url, stream=True)
        for block in r.iter_content(1024):
            if not block:
                break
            f.write(block)

def getImageFast(url, name=None, folder='./'):
    file = createFilename(url, name, folder)
    r = requests.get(url)
    i = Image.open(StringIO(r.content))
    i.save(file)

if __name__ == '__main__':
    # Uses Less Memory
    getImage('http://www.example.com/image.jpg')
    # Faster
    getImageFast('http://www.example.com/image.jpg')

В requestВнутренности getImage()основаны на ответ здесь и кишках getImageFast()основаны на ответ выше .


3

Я собираюсь опубликовать ответ, так как мне не хватает представителя, чтобы оставить комментарий, но с помощью wget, опубликованной Blairg23, вы также можете указать параметр out для пути.

 wget.download(url, out=path)

2

Это первый ответ, который появляется в поиске Google о том, как загрузить двоичный файл с запросами. Если вам нужно скачать произвольный файл с запросами, вы можете использовать:

import requests
url = 'https://s3.amazonaws.com/lab-data-collections/GoogleNews-vectors-negative300.bin.gz'
open('GoogleNews-vectors-negative300.bin.gz', 'wb').write(requests.get(url, allow_redirects=True).content)

1
Ницца! Это даже неявное .close(). Наверное, это лучший ответ на 2019 год.
— Даниэль В.

2

Вот как я это сделал

import requests
from PIL import Image
from io import BytesIO

url = 'your_url'
files = {'file': ("C:/Users/shadow/Downloads/black.jpeg", open('C:/Users/shadow/Downloads/black.jpeg', 'rb'),'image/jpg')}
response = requests.post(url, files=files)

img = Image.open(BytesIO(response.content))
img.show()

-1

Вы можете сделать что-то вроде этого:

import requests
import random

url = "https://images.pexels.com/photos/1308881/pexels-photo-1308881.jpeg? auto=compress&cs=tinysrgb&dpr=1&w=500"
name=random.randrange(1,1000)
filename=str(name)+".jpg"
response = requests.get(url)
if response.status_code.ok:
   with open(filename,'w') as f:
    f.write(response.content)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.