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
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.
to use r.raw you need to set stream=True
– clsung
Apr 23 '13 at 4:26