如何使用请求下载图像


367

我正在尝试使用python的requests模块从网络下载并保存图像。

这是我使用的(工作)代码:

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

Answers:


516

您可以使用response.rawfile对象,也可以遍历响应。

response.raw默认情况下,使用类似文件的对象不会解码压缩的响应(使用GZIP或deflate)。您可以通过将decode_content属性设置为Truerequests将其设置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=Truerequests不先将整个图像下载到内存中。


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']
马丁·彼得斯

1
要获取默认的128字节块,您需要对其本身进行迭代requests.Responsefor chunk in r: ...iter_content()没有a的调用chunk_size在1个字节的块中迭代
dtk 2015年

@dtk:谢谢,我将更新答案。发布答案后,迭代发生了变化
马丁·彼得斯

1
@KumZ有两个原因:response.ok从未被记录过,并且它对于任何1xx,2xx或3xx状态都会产生true,但是只有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
非常感谢您回来并回答这个问题。尽管另一个答案是
可行的

11
值得注意的是,很少有服务器将其映像设置为GZIP,因为映像已经具有自己的压缩功能。它起反作用,浪费CPU周期,几乎没有收益。因此,尽管这可能是文本内容(尤其是图像)的问题,但不是。
phette23 2014年

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)
Simin Jie

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我认为使用它会更Pythonicif response.ok:
EndermanAPM

5
对于任何1xx,2xx或3xx状态,response.ok均为True,但是只有200个响应具有@Martijn Pieters的响应主体,如上面的评论中所述
anndrey

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

12
这是因为您没有指定chunk_size默认值为1 的参数,所以一次iter_content将结果流迭代1个字节。请参阅文档python-requests.org/en/latest/api/…
CadentOrange 2013年

9
这还将整个响应加载到内存中,您可能需要避免这种情况。这里也没有用PIL,就with open(image_name, 'wb') as outfile: outfile.write(r.content)足够了。
马丁·皮特斯

3
PIL也不在标准库中,这使它的可移植性降低了。
jjj

2
@ZhenyiZhang iter_content很慢,因为您chunk_size的参数太小,如果将其增加到100k,则速度会更快。

这是最好的答案。并非总是最好将文件读取到内存中,但是OP指定了“图像”,这意味着文件通常小于4MB,因此对内存的影响很小。
克里斯·康兰

51

这可能比使用容易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
请注意,此答案适用于Python2。对于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

有两种主要方法:

  1. 使用.content(最简单/官方的)(请参见Zhenyi Zhang的答案):

    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(请参阅Martijn Pieters的答案):

    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.BytesIOImage)是第一个在Python 3.6上对我有用的答案。不要忘记from PIL import Image(和pip install Pillow)。
colllin '17

.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

我将发布答案,因为我没有足够的代表发表评论,但是使用Blairg23发布的wget,您还可以为路径提供out参数。

 wget.download(url, out=path)

2

这是谷歌搜索有关如何下载带有请求的二进制文件的第一个响应。如果您需要下载包含请求的任意文件,可以使用:

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年的最佳答案。
丹尼尔·W.19年

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.