【问题标题】:How to download image using requests如何使用请求下载图像
【发布时间】:2012-10-19 17:21:15
【问题描述】:

我正在尝试使用 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 的响应中使用什么属性吗?

【问题讨论】:

标签: python urllib2 python-requests


【解决方案1】:

您可以使用response.raw file object,或迭代响应。

默认情况下,使用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() method 和自定义块大小:

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就不会先将整个图像下载到内存中。

【讨论】:

  • 在您的回答的帮助下,我可以在文本文件中找到数据,我使用的步骤是r2 = requests.post(r.url, data); print r2.content。但现在我也想知道filename。他们有什么清洁的方式吗? -- 目前我在标题中找到了文件名 -- r2.headers['content-disposition'] 给我的输出为:'attachment; filename=DELS36532G290115.csi' 我正在为文件名解析这个字符串......他们有什么更干净的方法吗?
  • @GrijeshChauhan:是的,content-disposition 标头是这里的方法;使用cgi.parse_header()解析并获取参数; params = cgi.parse_header(r2.headers['content-disposition'])[1] 然后params['filename']
  • 要获取默认的 128 字节块,您需要 iterate over the requests.Response itself:for chunk in r: ...。在没有chunk_size 的情况下调用iter_content() 将是iterate in 1 byte chunks
  • @dtk:谢谢,我会更新答案。迭代changed after I posted my answer.
  • @KumZ 有两个原因:response.ok 从未被记录在案,它对任何 1xx、2xx 或 3xx 状态都产生 true,但只有 200 响应具有响应正文。
【解决方案2】:

从请求中获取一个类文件对象并将其复制到一个文件中。这也将避免一次将整个内容读入内存。

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

【讨论】:

  • 非常感谢您回来回答这个问题。虽然另一个答案是有效的,但这个答案更简单
  • 值得注意的是,很少有服务器将其图像设置为 GZIP,因为图像已经具有自己的压缩功能。它会适得其反,浪费 CPU 周期而没有什么好处。因此,虽然这可能是文本内容的问题,特别是图像,但它不是。
  • 有什么办法可以访问原始文件名
  • @phette23 另外值得注意的是,Google PageSpeed 会默认报告并执行此操作。
  • 应该在shutil.copyfileobj(response.raw, out_file)之前设置r.raw.decode_content = True,因为by default, decode compressed responses (with GZIP or deflate),所以你会得到一个零文件的图像。
【解决方案3】:

这个怎么样,一个快速的解决方案。

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)

【讨论】:

  • 你是什么意思! f = open("/Users/apple/Desktop/sample.jpg", 'wb')这条路是什么意思!?我要下载图片
  • 在指定的路径中打开一个文件描述符,可以写入图像文件。
  • @AndrewGlazkov 我认为使用if response.ok: 会更Pythonic
  • response.ok 对于任何 1xx、2xx 或 3xx 状态都是 True,但只有 200 响应具有上述 cmets 中提到的 @Martijn Pieters 的响应正文
【解决方案4】:

我同样需要使用请求下载图像。我首先尝试了 Martijn Pieters 的答案,效果很好。但是当我对这个简单的函数进行概要分析时,我发现与urlliburllib2 相比,它使用了很多函数调用。

然后我尝试了请求模块作者的way recommended

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

【讨论】:

  • 这是因为您没有指定默认为 1 的 chunk_size 参数,因此 iter_content 一次迭代结果流 1 个字节。请参阅文档python-requests.org/en/latest/api/…
  • 这也会将整个响应加载到内存中,您可能希望避免这种情况。这里也没有PILwith open(image_name, 'wb') as outfile: outfile.write(r.content)就够了。
  • PIL 也不在标准库中,这使得它的可移植性有所降低。
  • @ZhenyiZhang iter_content 很慢,因为你的chunk_size 太小了,如果你增加到100k 会快很多。
  • 根据请求作者http://docs.python-requests.org/en/latest/user/quickstart/#binary-response-content,现在from StringIO import StringIO似乎是from io import BytesIO
【解决方案5】:

这可能比使用requests 更容易。这是我唯一一次建议不要使用 requests 来做 HTTP 的事情。

两个班轮使用urllib:

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

还有一个不错的 Python 模块,名为 wget,非常易于使用。找到here

这证明了设计的简单性:

>>> 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 的好处
  • 请注意,此答案适用于 Python 2。对于 Python 3,您需要执行 urllib.request.urlretrieve("http://example.com", "file.ext")
  • 谢谢@Husky。已更新。
  • 我们可以在这里压缩图像大小吗? @布莱尔格23
  • @Faiyaj 不,这只是wget,没有压缩文件。
【解决方案6】:

以下代码 sn -p 下载文件。

文件以指定 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)

【讨论】:

    【解决方案7】:

    主要有两种方式:

    1. 使用.content(最简单/官方)(见Zhenyi Zhang's answer):

      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's answer):

      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.
      

    两者的时间没有明显差异。

    【讨论】:

    • 我尝试了一堆答案,而您的1. 答案(使用io.BytesIOImage)是第一个在Python 3.6 上为我工作的答案。不要忘记from PIL import Image(和pip install Pillow)。
    • .content 和 .raw 有什么不同?
    【解决方案8】:

    像导入图片和请求一样简单

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

    【讨论】:

      【解决方案9】:

      这是一个更加用户友好的答案,仍然使用流媒体。

      只需定义这些函数并调用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')
      

      getImage()request 胆量基于here 的答案,getImageFast() 的胆量基于above 的答案。

      【讨论】:

        【解决方案10】:

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

         wget.download(url, out=path)
        

        【讨论】:

          【解决方案11】:

          我就是这样做的

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

          【讨论】:

            【解决方案12】:

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

            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)
            

            【讨论】:

            • 不错!它甚至有一个隐含的.close()。我猜这是截至 2019 年的最佳答案。
            【解决方案13】:

            我的方法是使用 response.content (blob) 并以二进制模式保存到文件中

            img_blob = requests.get(url, timeout=5).content
                 with open(destination + '/' + title, 'wb') as img_file:
                     img_file.write(img_blob)
            

            查看我的python project,它根据关键字从 unsplash.com 下载图像。

            【讨论】:

              【解决方案14】:

              你可以这样做:

              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)
              

              【讨论】:

                【解决方案15】:

                同意Blairg23 的观点,即使用urllib.request.urlretrieve 是最简单的解决方案之一。

                我想在这里指出一点。有时它不会下载任何东西,因为请求是通过脚本(bot)发送的,如果你想从谷歌图片或其他搜索引擎解析图片,你需要先通过user-agent请求headers,然后再下载图片,否则会阻塞请求并抛出错误。

                传递user-agent并下载图片:

                opener=urllib.request.build_opener()
                opener.addheaders=[('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19582')]
                urllib.request.install_opener(opener)
                
                urllib.request.urlretrieve(URL, 'image_name.jpg')
                

                Code in the online IDE that scrapes and downloads images from Google images 使用 requestsbs4urllib.requests


                或者,如果您的目标是从 Google、Bing、Yahoo!、DuckDuckGo(和其他搜索引擎)等搜索引擎抓取图像,那么您可以使用 SerpApi。这是一个带有免费计划的付费 API。

                最大的不同是无需弄清楚如何绕过搜索引擎的阻止或如何从 HTML 或 JavaScript 中提取某些部分,因为它已经为最终用户完成了。

                要集成的示例代码:

                import os, urllib.request
                from serpapi import GoogleSearch
                
                params = {
                  "api_key": os.getenv("API_KEY"),
                  "engine": "google",
                  "q": "pexels cat",
                  "tbm": "isch"
                }
                
                search = GoogleSearch(params)
                results = search.get_dict()
                
                print(json.dumps(results['images_results'], indent=2, ensure_ascii=False))
                
                # download images 
                for index, image in enumerate(results['images_results']):
                
                    # print(f'Downloading {index} image...')
                    
                    opener=urllib.request.build_opener()
                    opener.addheaders=[('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19582')]
                    urllib.request.install_opener(opener)
                
                    # saves original res image to the SerpApi_Images folder and add index to the end of file name
                    urllib.request.urlretrieve(image['original'], f'SerpApi_Images/original_size_img_{index}.jpg')
                
                -----------
                '''
                ]
                  # other images
                  {
                    "position": 100, # 100 image
                    "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQK62dIkDjNCvEgmGU6GGFZcpVWwX-p3FsYSg&usqp=CAU",
                    "source": "homewardboundnj.org",
                    "title": "pexels-helena-lopes-1931367 - Homeward Bound Pet Adoption Center",
                    "link": "https://homewardboundnj.org/upcoming-event/black-cat-appreciation-day/pexels-helena-lopes-1931367/",
                    "original": "https://homewardboundnj.org/wp-content/uploads/2020/07/pexels-helena-lopes-1931367.jpg",
                    "is_product": false
                  }
                ]
                '''
                

                免责声明,我为 SerpApi 工作。

                【讨论】:

                  【解决方案16】:

                  下载图片

                  import requests
                  Picture_request = requests.get(url)
                  

                  【讨论】:

                  • 如果一切都这么简单就好了。不幸的是,您示例中的代码不保存图像。它可以打开图像,就是这样。
                  猜你喜欢
                  • 1970-01-01
                  • 2016-10-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-01-22
                  • 2019-11-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多