リクエストを使用して画像をダウンロードする方法


368

私は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かを教えていただけますか?


16
r.rawを使用するには、stream = Trueを設定する必要があります
2013

回答:


517

response.rawファイルオブジェクトを使用するか、応答を反復処理できます

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()メソッドを使用します。

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、2015

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: ...。will iter_content()なしで呼び出すchunk_size、1バイトのチャンクで繰り返し処理されます
dtk

@dtk:ありがとう、答えを更新します。回答を投稿した後、反復が変更されました
Martijn Pieters

1
@KumZ 2つの理由:response.ok文書化されたことはなく、1xx、2xx、3xxのいずれのステータスでもtrueを生成しますが、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するように設定されているサーバーはほとんどないことに注意してください。これは逆効果で、CPUサイクルを浪費し、ほとんどメリットがありません。したがって、これはテキストコンテンツの問題である可能性がありますが、特に画像の問題ではありません。
phette23 2014

3
元のファイル名にアクセスする方法はありますか
mahes

@ phette23 Google PageSpeedがレポートし、デフォルトでそれを行うことも注目に値します。
2016年

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 2016年

@AndrewGlazkov私はそれを使用する方がよりPythonicになると思いますif response.ok:
EndermanAPM

5
response.okは、1xx、2xx、3xxのいずれのステータスでもTrueですが、上記のコメントで言及されている@Martijn Pietersのように200の応答のみが応答本文を持っています
annndrey

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

13
これは、chunk_sizeデフォルトで1になるパラメーターを指定していないためiter_content、結果ストリームを一度に1バイトずつ繰り返します。ドキュメントpython-requests.org/en/latest/api/…を参照してください。
CadentOrange 2013年

10
これにより、応答全体がメモリに読み込まれるため、回避する必要があります。PILここでも使用する必要はありませんが、それでwith open(image_name, 'wb') as outfile: outfile.write(r.content)十分です。
Martijn Pieters

3
PILまた、標準ライブラリにはないため、移植性が少し低下します。
JJJ

2
@ZhenyiZhang iter_contentchunk_size、小さすぎるために遅くなります。100kに増やすと、はるかに速くなります。

これが最良の答えです。ファイルをメモリに読み込むのが常に最善であるとは限りませんが、OPが「イメージ」を指定した場合、ファイルは通常4MB未満になるため、メモリにささいな影響を与えます。
Chris Conlan、2018年

51

これは、を使用するよりも簡単かもしれませんrequests。これは、requestsHTTPの処理に使用しないことをお勧めする唯一の時間です。

使用する2つのライナーurllib

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

wgetとても使いやすいという名前の素敵なPythonモジュールもあります。ここで見つかりまし

これは、設計が単純であることを示しています。

>>> 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用であることに注意してください。Python3の場合は、実行する必要があります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

主な方法は2つあります。

  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. 使用.rawMartijn 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

.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を使用して、パスの出力パラメーターを指定することもできます。

 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年の時点での最良の答えです。
ダニエルW.

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.