How to download image using requests


How to download image using requests



I'm trying to download and save an image from the web using python's requests module.


requests



Here is the (working) code I used:


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



Here is the new (non-working) code using requests:


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)



Can you help me on what attribute from the response to use from requests?


requests





to use r.raw you need to set stream=True
– clsung
Apr 23 '13 at 4:26




10 Answers
10



You can either use the response.raw file object, or iterate over the response.


response.raw



To use the response.raw file-like object will not, by default, decode compressed responses (with GZIP or deflate). You can force it to decompress for you anyway by setting the decode_content attribute to True (requests sets it to False to control decoding itself). You can then use shutil.copyfileobj() to have Python stream the data to a file object:


response.raw


decode_content


True


requests


False


shutil.copyfileobj()


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)



To iterate over the response use a loop; iterating like this ensures that data is decompressed by this stage:


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)



This'll read the data in 128 byte chunks; if you feel another chunk size works better, use the Response.iter_content() method with a custom chunk size:


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)



Note that you need to open the destination file in binary mode to ensure python doesn't try and translate newlines for you. We also set stream=True so that requests doesn't download the whole image into memory first.


stream=True


requests





With the help of your answer I could able to find data in text file, steps I used are r2 = requests.post(r.url, data); print r2.content. But now I also want to know filename. is their any cleaned way? -- presently I found file name in header -- r2.headers['content-disposition'] that gives me output as: 'attachment; filename=DELS36532G290115.csi' I am parsing this string for filename... is their any cleaner way?
– Grijesh Chauhan
Jan 29 '15 at 10:39


r2 = requests.post(r.url, data); print r2.content


filename


r2.headers['content-disposition']


'attachment; filename=DELS36532G290115.csi'





@GrijeshChauhan: yes, the content-disposition header is the way to go here; use cgi.parse_header() to parse it and get the parameters; params = cgi.parse_header(r2.headers['content-disposition'])[1] then params['filename'].
– Martijn Pieters
Jan 29 '15 at 10:41



content-disposition


cgi.parse_header()


params = cgi.parse_header(r2.headers['content-disposition'])[1]


params['filename']





To get the default 128 byte chunks, you need to iterate over the requests.Response itself: for chunk in r: .... Calling iter_content() without a chunk_size will iterate in 1 byte chunks.
– dtk
Jun 2 '15 at 23:23



requests.Response


for chunk in r: ...


iter_content()


chunk_size





@dtk: thanks, I'll update the answer. Iteration changed after I posted my answer.
– Martijn Pieters
Jun 25 '15 at 10:37





@KumZ two reasons: response.ok was never documented, and it produces true for any 1xx, 2xx or 3xx status, but only a 200 response has a response body.
– Martijn Pieters
Nov 23 '16 at 19:31


response.ok



Get a file-like object from the request and copy it to a file. This will also avoid reading the whole thing into memory at once.


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





Thank you so much for coming back and answering this. Though the other answer is works, this one is leaps and bounds simpler
– dkroy
Aug 6 '13 at 4:04





It's worth noting that few servers are set to GZIP their images because images already have their own compression. It's counterproductive, wastes CPU cycles with little benefit. So while this may be an issue with text content, specifically with images it's not.
– phette23
Sep 11 '14 at 4:19





is there any way we can access the original filename
– user2332665
Mar 6 '16 at 13:51





Thank you so much..just what I was looking for.
– Vipul Jain
Sep 9 '16 at 19:32





Should set r.raw.decode_content = True before shutil.copyfileobj(response.raw, out_file) because by default, decode compressed responses (with GZIP or deflate), so you will get a zero-file image.
– Simin Jie
Dec 29 '16 at 3:42



r.raw.decode_content = True


shutil.copyfileobj(response.raw, out_file)


by default, decode compressed responses (with GZIP or deflate)



How about this, a quick solution.


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)





what do you mean with ! f = open("/Users/apple/Desktop/sample.jpg", 'wb') what do you mean with this path !? i want to download image
– smile
Nov 2 '16 at 17:48


f = open("/Users/apple/Desktop/sample.jpg", 'wb')





That opens a file descriptor in the path specified to which the image file can be written.
– kiranbkrishna
Nov 3 '16 at 10:07





I don't understand why this answer have so few votes. As for me it's a best, most elegant and pythonic answer of all.
– Andrew Glazkov
Sep 29 '17 at 15:59





Simple, elegant and best. Awesome. thanks for that
– sdevgd
Oct 22 '17 at 14:33



I have the same need for downloading images using requests. I first tried the answer of Martijn Pieters, and it works well. But when I did a profile on this simple function, I found that it uses so many function calls compared to urllib and urllib2.



I then tried the way recommended by the author of requests module:


import requests
from PIL import Image
from StringIO import StringIO

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



This much more reduced the number of function calls, thus speeded up my application.
Here is the code of my profiler and the result.


#!/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()')



The result for testRequest:


343080 function calls (343068 primitive calls) in 2.580 seconds



And the result for testRequest2:


3129 function calls (3105 primitive calls) in 0.024 seconds





This is because you've not specified the chunk_size parameter which defaults to 1, so iter_content is iterating over the result stream 1 byte at a time. See the documentation python-requests.org/en/latest/api/….
– CadentOrange
Oct 17 '13 at 15:53


chunk_size


iter_content





This also loads the whole response into memory, which you may want to avoid. There is no to use PIL here either, just with open(image_name, 'wb') as outfile: outfile.write(r.content) is enough.
– Martijn Pieters
Jan 9 '14 at 13:25


PIL


with open(image_name, 'wb') as outfile: outfile.write(r.content)





PIL is also not in the standard library making this a bit less portable.
– jjj
Dec 22 '15 at 21:19


PIL





This should be the accepted answer.
– Axe
Jul 29 '16 at 19:14





@ZhenyiZhang iter_content is slow because your chunk_size is too small, if you increase it to 100k it will be much faster.
– Wang
Feb 3 '17 at 17:25


iter_content


chunk_size



This might be easier than using requests. This is the only time I'll ever suggest not using requests to do HTTP stuff.


requests


requests



Two liner using urllib:


urllib


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



There is also a nice Python module named wget that is pretty easy to use. Found here.


wget



This demonstrates the simplicity of the design:


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



Enjoy.



Edit: You can also add an out parameter to specify a path.


out


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



Following code snippet downloads a file.



The file is saved with its filename as in specified url.


import requests

url = "http://beispiel.dort/ichbineinbild.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)



There are 2 main ways:



Using .content (simplest/official) (see Zhenyi Zhang's answer):


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



Using .raw (see Martijn Pieters's answer):


.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.



Timing both shows no noticeable difference.





I tried a bunch of answers, and your 1. answer (using io.BytesIO and Image) was the first one that worked for me on Python 3.6. Don't forget from PIL import Image (and pip install Pillow).
– colllin
Dec 4 '17 at 23:53



1.


io.BytesIO


Image


from PIL import Image


pip install Pillow



Here is a more user-friendly answer that still uses streaming.



Just define these functions and call getImage(). It will use the same file name as the url and write to the current directory by default, but both can be changed.


getImage()


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



The request guts of getImage() are based on the answer here and the guts of getImageFast() are based on the answer above.


request


getImage()


getImageFast()



I'm going to post an answer as I don't have enough rep to make a comment, but with wget as posted by Blairg23, you can also provide an out parameter for the path.


wget.download(url, out=path)





You can also edit answers or suggest edits.
– Blairg23
Jan 15 '17 at 5:39



when I try to run the below code,the image is getting downaloded but the size is always confined to 34 KB.


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)



And aslo please let me know what is settings.STATICMAP_URL.format(**data),I'm using my usl in place of settings.STATICMAP_URL.format(**data)





Since I don't have access to comment,I posted my query in answer section.
– Logic lover
Jul 3 at 5:27







By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

PHP contact form sending but not receiving emails

PHP parse/syntax errors; and how to solve them?

iOS Top Alignment constraint based on screen (superview) height