Cara mengunduh gambar menggunakan permintaan

368

Saya mencoba mengunduh dan menyimpan gambar dari web menggunakan requestsmodul python .

Berikut adalah kode (yang berfungsi) yang saya gunakan:

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

Berikut adalah kode baru (tidak berfungsi) menggunakan 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)

Bisakah Anda membantu saya tentang atribut dari respons yang digunakan requests?

shkschneider
sumber
16
untuk menggunakan r.raw, Anda perlu mengatur stream = True
clsung
Apakah ini menjawab pertanyaan Anda? Unduh file besar dengan python dengan permintaan
AMC

Jawaban:

517

Anda bisa menggunakan response.rawobjek file , atau beralih pada respons.

Untuk menggunakan response.rawobjek seperti file tidak akan, secara default, mendekode respons terkompresi (dengan GZIP atau deflate). Anda dapat memaksanya untuk mendekompresi untuk Anda dengan mengatur decode_contentatribut ke True( requestsmengaturnya Falseuntuk mengontrol decoding itu sendiri). Anda bisa menggunakan shutil.copyfileobj()Python stream data ke objek file:

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)        

Untuk beralih pada respons gunakan loop; iterasi seperti ini memastikan bahwa data didekompresi pada tahap ini:

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)

Ini akan membaca data dalam 128 byte chunks; jika Anda merasa ukuran chunk lain berfungsi lebih baik, gunakan Response.iter_content()metode ini dengan ukuran chunk khusus:

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)

Perhatikan bahwa Anda perlu membuka file tujuan dalam mode biner untuk memastikan python tidak mencoba dan menerjemahkan baris baru untuk Anda. Kami juga mengatur stream=Trueagar requeststidak mengunduh seluruh gambar ke dalam memori terlebih dahulu.

Martijn Pieters
sumber
2
Dengan bantuan jawaban Anda, saya dapat menemukan data dalam file teks, langkah-langkah yang saya gunakan adalah r2 = requests.post(r.url, data); print r2.content. Tapi sekarang saya juga ingin tahu filename. Apakah cara mereka dibersihkan? - saat ini saya menemukan nama file di header - r2.headers['content-disposition'] yang memberi saya output sebagai: 'attachment; filename=DELS36532G290115.csi' Saya parsing string ini untuk nama file ... apakah cara mereka lebih bersih?
Grijesh Chauhan
6
@GrijeshChauhan: ya, content-dispositiontajuk adalah cara untuk pergi ke sini; gunakan cgi.parse_header()untuk menguraikannya dan mendapatkan parameter; params = cgi.parse_header(r2.headers['content-disposition'])[1]kemudian params['filename'].
Martijn Pieters
1
Untuk mendapatkan default 128 potongan byte, Anda perlu iterate atas requests.Responseitu sendiri : for chunk in r: .... Memanggil iter_content()tanpa surat chunk_sizewasiat akan dilakukan dalam potongan 1 byte .
dtk
@ dtk: terima kasih, saya akan memperbarui jawabannya. Iterasi berubah setelah saya memposting jawaban saya .
Martijn Pieters
1
@KumZ dua alasan: response.oktidak pernah didokumentasikan, dan ini menghasilkan true untuk status 1xx, 2xx atau 3xx, tetapi hanya 200 tanggapan yang memiliki badan tanggapan.
Martijn Pieters
232

Dapatkan objek seperti file dari permintaan dan salin ke file. Ini juga akan menghindari membaca semuanya ke dalam memori sekaligus.

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
Oleh Prypin
sumber
14
Terima kasih banyak telah datang kembali dan menjawab ini. Meskipun jawaban yang lain adalah berhasil, yang ini lebih cepat dan lebih mudah
dkroy
11
Perlu dicatat bahwa beberapa server diatur untuk GZIP gambar mereka karena gambar sudah memiliki kompresi sendiri. Ini kontraproduktif, limbah siklus CPU dengan sedikit manfaat. Jadi, sementara ini mungkin masalah dengan konten teks, khususnya dengan gambar itu bukan.
phette23
3
apakah ada cara apa pun untuk mengakses nama file asli
mahes
@ phette23 Perlu dicatat bahwa Google PageSpeed ​​melaporkan dan melakukannya secara default.
Wernight
8
Harus diatur r.raw.decode_content = Truesebelumnya shutil.copyfileobj(response.raw, out_file)karena by default, decode compressed responses (with GZIP or deflate), jadi Anda akan mendapatkan gambar nol file.
Simin Jie
166

Bagaimana dengan ini, solusi cepat.

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)
kiranbkrishna
sumber
1
apa maksudmu dengan! f = open("/Users/apple/Desktop/sample.jpg", 'wb')apa maksudmu dengan jalan ini !? saya ingin mengunduh gambar
senyum
3
Itu membuka deskriptor file di jalur yang ditentukan di mana file gambar dapat ditulis.
kiranbkrishna
@AndrewGlazkov Saya pikir akan lebih Pythonic untuk digunakanif response.ok:
EndermanAPM
5
response.ok Benar untuk setiap status 1xx, 2xx atau 3xx, tetapi hanya 200 respons yang memiliki badan respons seperti @Martijn Pieters yang disebutkan dalam komentar di atas
annndrey
75

Saya memiliki kebutuhan yang sama untuk mengunduh gambar menggunakan permintaan. Saya pertama kali mencoba jawaban Martijn Pieters, dan itu bekerja dengan baik. Tetapi ketika saya melakukan profil pada fungsi sederhana ini, saya menemukan bahwa ia menggunakan begitu banyak panggilan fungsi dibandingkan dengan urllib dan urllib2.

Saya kemudian mencoba cara yang direkomendasikan oleh penulis modul permintaan:

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

Ini jauh lebih mengurangi jumlah panggilan fungsi, sehingga mempercepat aplikasi saya. Ini kode profiler saya dan hasilnya.

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

Hasil untuk testRequest:

343080 function calls (343068 primitive calls) in 2.580 seconds

Dan hasil untuk testRequest2:

3129 function calls (3105 primitive calls) in 0.024 seconds
Zhenyi Zhang
sumber
13
Ini karena Anda belum menentukan chunk_sizeparameter yang default ke 1, jadi iter_contentiterasi dari aliran hasil 1 byte sekaligus. Lihat dokumentasi python-requests.org/en/latest/api/… .
CadentOrange
10
Ini juga memuat seluruh respons ke dalam memori, yang mungkin ingin Anda hindari. Tidak ada untuk digunakan di PILsini, with open(image_name, 'wb') as outfile: outfile.write(r.content)cukup saja.
Martijn Pieters
3
PILjuga tidak ada di perpustakaan standar membuat ini sedikit kurang portabel.
jjj
2
@ZhenyiZhang iter_contentlambat karena Anda chunk_sizeterlalu kecil, jika Anda meningkatkannya menjadi 100k akan jauh lebih cepat.
Wang
Ini jawaban terbaik. Tidak selalu terbaik untuk membaca file ke dalam memori, tetapi OP menentukan "gambar" yang berarti file biasanya akan kurang dari 4MB, sehingga memiliki dampak sepele pada memori.
Chris Conlan
52

Ini mungkin lebih mudah daripada menggunakan requests. Ini adalah satu-satunya waktu saya menyarankan tidak menggunakan requestsuntuk melakukan hal-hal HTTP.

Dua liner menggunakan urllib:

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

Ada juga modul Python bagus bernama wgetyang cukup mudah digunakan. Ditemukan di sini .

Ini menunjukkan kesederhanaan desain:

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

Nikmati.

Sunting: Anda juga dapat menambahkan outparameter untuk menentukan jalur.

>>> out_filepath = <output_filepath>    
>>> filename = wget.download(url, out=out_filepath)
Blairg23
sumber
Saya menggunakan wgettanpa kerepotan. Terima kasih telah menyatakan manfaat menggunakanurllib3
h3xh4wk
1
Perhatikan bahwa jawaban ini untuk Python 2. Untuk Python 3 yang perlu Anda lakukan urllib.request.urlretrieve("http://example.com", "file.ext").
Husky
1
Terima kasih @ Husky. Diperbarui.
Blairg23
28

Cuplikan kode berikut mengunduh file.

File disimpan dengan nama file seperti di url yang ditentukan.

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)
Katja Süss
sumber
16

Ada 2 cara utama:

  1. Menggunakan .content(paling sederhana / resmi) (lihat jawaban 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. Menggunakan .raw(lihat jawaban 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.

Waktu keduanya tidak menunjukkan perbedaan yang nyata.

Wernight
sumber
2
Saya mencoba banyak jawaban, dan 1.jawaban Anda (menggunakan io.BytesIOdan Image) adalah yang pertama yang bekerja untuk saya di Python 3.6. Jangan lupa from PIL import Image(dan pip install Pillow).
colllin
Apa yang berbeda antara .content dan .raw?
foxiris
13

Semudah mengimpor Gambar dan permintaan

from PIL import Image
import requests

img = Image.open(requests.get(url, stream = True).raw)
img.save('img1.jpg')
Riccardo D
sumber
4

Berikut adalah jawaban yang lebih ramah pengguna yang masih menggunakan streaming.

Cukup tentukan fungsi-fungsi ini dan panggil getImage(). Ini akan menggunakan nama file yang sama dengan url dan menulis ke direktori saat ini secara default, tetapi keduanya dapat diubah.

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 requestkeberanian getImage()didasarkan pada jawaban di sini dan keberanian dari getImageFast()didasarkan pada jawaban di atas .

Chris Redford
sumber
3

Saya akan memposting jawaban karena saya tidak punya cukup rep untuk membuat komentar, tetapi dengan wget seperti yang diposting oleh Blairg23, Anda juga dapat memberikan parameter keluar untuk jalur.

 wget.download(url, out=path)
justincc
sumber
2

Ini adalah respons pertama yang muncul untuk pencarian google tentang cara mengunduh file biner dengan permintaan. Jika Anda perlu mengunduh file sewenang-wenang dengan permintaan, Anda dapat menggunakan:

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)
duhaime
sumber
1
Bagus! Itu bahkan memiliki implisit .close(). Saya kira ini adalah jawaban terbaik pada 2019.
Daniel W.
2

Beginilah cara saya melakukannya

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()
Harshit Singhai
sumber
-1

Anda dapat melakukan sesuatu seperti ini:

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)
Jyotiprakash Das
sumber