Як завантажити зображення за допомогою запитів


367

Я намагаюся завантажити та зберегти зображення з Інтернету за допомогою 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?


15
для використання r.raw вам потрібно встановити stream = True
clsung

Чи відповідає це на ваше запитання? Завантажте великий файл у python із запитами
AMC

Відповіді:


516

Можна або використовувати response.rawоб’єкт файлу , або повторити відповідь.

Використання response.rawфайлоподібного об'єкта за замовчуванням не декодує стислі відповіді (за допомогою GZIP або дефляції). Ви можете змусити його декомпресувати для вас будь-який спосіб, встановивши 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' Я розбираю цей рядок на ім'я файлу ... чи є їх чистішим способом?
Grijesh Chauhan

6
@GrijeshChauhan: так, content-dispositionзаголовок - це спосіб пройти сюди; використовувати cgi.parse_header()для розбору та отримання параметрів; params = cgi.parse_header(r2.headers['content-disposition'])[1]то params['filename'].
Martijn Pieters

1
Щоб отримати по замовчуванням 128 байт скибки с, вам потрібно перебрати на requests.Responseсебе : for chunk in r: .... Виклик iter_content()без chunk_sizeзаповіту повторюється в 1 байт .
dtk

@dtk: спасибі, я оновлю відповідь. Ітерація змінилася після того, як я опублікував свою відповідь .
Martijn Pieters

1
@KumZ дві причини: response.okніколи не було задокументовано, і це відповідає дійсності для будь-якого 1xx, 2xx або 3xx статусу, але лише відповідь на 200 має тіло відповідей.
Martijn Pieters

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 ​​звітує і робить це за замовчуванням.
Wernight

8
Якщо встановити r.raw.decode_content = Trueдо того, shutil.copyfileobj(response.raw, out_file)бо by default, decode compressed responses (with GZIP or deflate), таким чином , ви отримаєте нульовий файл зображення.
Сімін Джі

166

Як щодо цього, швидке рішення.

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
Це відкриває дескриптор файлу в шляху, вказаному в який може бути записаний файл зображення.
kiranbkrishna

@AndrewGlazkov Я думаю, що це було б більше Pythonic використовуватиif response.ok:
EndermanAPM

5
response.ok Істинно для будь-якого 1xx, 2xx або 3xx статусу, але лише 200 відповідей мають орган відповідей, як @Martijn Pieters, згаданий у коментарях вище
anndrey

75

У мене така ж потреба у завантаженні зображень за допомогою запитів. Я спершу спробував відповідь Мартіна Пітерса, і це працює добре. Але коли я зробив профіль на цій простій функції, я виявив, що в ній використовується стільки викликів функцій порівняно з 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

12
Це пояснюється тим, що ви не вказали chunk_sizeпараметр, який за замовчуванням iter_contentдорівнює 1, так само він повторюється над потоком результатів 1 байтом одночасно. Дивіться документацію python-requests.org/en/latest/api/… .
CadentOrange

9
Це також завантажує всю пам'ять у пам'ять, чого ви можете уникнути. Тут також використовувати немає PIL, просто with open(image_name, 'wb') as outfile: outfile.write(r.content)достатньо.
Martijn Pieters

3
PILтакож немає в стандартній бібліотеці, що робить це трохи менш портативним.
jjj

2
@ZhenyiZhang iter_contentповільний, тому що ваш chunk_sizeзанадто маленький, якщо ви збільшите його до 100k, це буде набагато швидше.
Ван

Це найкраща відповідь. Не завжди найкраще читати файл у пам'яті, але "образи", задані ОП, тобто файли, як правило, становлять менше 4 МБ, тим самим маючи тривіальний вплив на пам'ять.
Кріс Конлан

51

Це може бути простіше, ніж використання requests. Це єдиний раз, коли я пропоную не використовуватиrequests HTTP-матеріали.

Два вкладиші, використовуючи 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
Дякую @Husky. Оновлено.
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

Є два основні способи:

  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 Imagepip 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, ви також можете вказати параметр для шляху.

 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.