İstekleri kullanarak resim nasıl indirilir


368

Python requestsmodülünü kullanarak web'den bir resim indirmeye ve kaydetmeye çalışıyorum .

İşte kullandığım (çalışma) kodu:

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

İşte yeni (çalışmayan) kodu kullanarak 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)

Kullanılacak yanıtın hangi özelliğinden bana yardımcı olabilir misiniz requests?


15
r.raw kullanmak için stream = True
clsung 23:13

Bu sorunuza cevap veriyor mu? Büyük dosyayı istekleri ile piton indir
AMC

Yanıtlar:


516

response.rawDosya nesnesini kullanabilir veya yanıtı yineleyebilirsiniz.

response.rawDosya benzeri nesneyi kullanmak varsayılan olarak sıkıştırılmış yanıtların (GZIP veya deflate ile) kodunu çözmez. decode_contentÖzniteliği olarak ayarlayarak yine de sizin için sıkıştırmasını açmaya zorlayabilirsiniz True( kod çözmenin kendisini kontrol requestsedecek Falseşekilde ayarlar ). Daha sonra shutil.copyfileobj()Python'un verileri bir dosya nesnesine akışını sağlamak için kullanabilirsiniz :

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)        

Yanıt üzerinde yineleme yapmak için bir döngü kullanın; bu şekilde yineleme, verilerin bu aşamada açılmasını sağlar:

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)

Bu, verileri 128 baytlık parçalar halinde okuyacaktır; başka bir yığın boyutunun daha iyi çalıştığını düşünüyorsanız, Response.iter_content()yöntemi özel bir yığın boyutuyla kullanın:

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'un sizin için yeni satırları çevirmeyi denemediğinden emin olmak için hedef dosyayı ikili modda açmanız gerektiğini unutmayın. Biz de set stream=Trueböylece requestsilk belleğe tüm görüntüyü indirmez.


2
Cevabınızın yardımıyla metin dosyasında veri bulabildim, kullandığım adımlar r2 = requests.post(r.url, data); print r2.content. Ama şimdi de bilmek istiyorum filename. temizlenmiş bir yolu var mı? - şu anda başlıkta dosya adı bulundu - r2.headers['content-disposition'] bana çıktı verir: 'attachment; filename=DELS36532G290115.csi' Ben dosya adı için bu dize ayrıştırıyorum ... onların daha temiz bir yolu var mı?
Grijesh Chauhan

6
@GrijeshChauhan: evet, content-dispositionbaşlık buraya gitmenin yolu; cgi.parse_header()ayrıştırmak ve parametreleri almak için kullanın ; params = cgi.parse_header(r2.headers['content-disposition'])[1]sonra params['filename'].
Martijn Pieters

1
Varsayılan 128 bayt parçalarını almak için, gerek içinde yineleme requests.Responsekendini : for chunk in r: .... 1 baytlık parçalar halinde irade iter_content()olmadan çağrı . chunk_size
dtk

@dtk: teşekkürler, cevabı güncelleyeceğim. Cevabımı gönderdikten sonra yineleme değişti .
Martijn Pieters

1
@KumZ iki nedeni: response.okasla belgelenmedi ve herhangi bir 1xx, 2xx veya 3xx durumu için doğru üretiyor, ancak sadece 200 yanıtın bir yanıt gövdesi var.
Martijn Pieters

232

İstekten dosya benzeri bir nesne alın ve bir dosyaya kopyalayın. Bu aynı zamanda her şeyi aynı anda belleğe okumaktan kaçınacaktır.

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
Geri gelip cevap verdiğin için çok teşekkür ederim. Diğer cevap işe yarıyor olsa da, bu bir sıçrama ve daha basit sınırlar
dkroy

11
Görüntülerin zaten kendi sıkıştırmaları olduğu için, birkaç sunucunun görüntülerini GZIP olarak ayarladığını belirtmek gerekir. Karşılıksızdır, CPU döngülerini çok az fayda ile harcar. Bu, metin içeriğiyle ilgili bir sorun olsa da, özellikle resimlerle ilgili bir sorun değildir.
phette23

3
orijinal dosya adına erişebilmemiz için herhangi bir yol var mı
mahes

@ phette23 Google PageSpeed'in raporladığını ve bunu varsayılan olarak yaptığını da belirtmek gerekir.
Wernight

8
r.raw.decode_content = TrueÖnceden ayarlanmalıdır shutil.copyfileobj(response.raw, out_file)çünkü by default, decode compressed responses (with GZIP or deflate)sıfır dosya görüntüsü alacaksınız.
Simin Jie

166

Buna ne dersin, hızlı bir çözüm.

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
ne demek istiyorsun! f = open("/Users/apple/Desktop/sample.jpg", 'wb')bu yolla ne demek istiyorsun? görüntüyü indirmek istiyorum
gülümseme

3
Bu, görüntü dosyasının yazılabileceği yolda bir dosya tanımlayıcı açar.
kiranbkrishna

@AndrewGlazkov Bence kullanmak daha Pythonic olacağını düşünüyorumif response.ok:
EndermanAPM

5
response.ok, herhangi bir 1xx, 2xx veya 3xx durumu için Doğru'dur, ancak yukarıdaki yorumlarda belirtilen @Martijn Pieters gibi yalnızca 200 yanıtın bir yanıt gövdesi vardır
annndrey

75

Aynı istekleri kullanarak görüntüleri indirmek için ihtiyacım var. Önce Martijn Pieters'ın cevabını denedim ve iyi çalışıyor. Ancak bu basit işlev hakkında bir profil yaptığımda, urllib ve urllib2 ile karşılaştırıldığında çok fazla işlev çağrısı kullandığını buldum.

Sonra istek modülü yazarı tarafından önerilen şekilde denedim :

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

Bu, işlev çağrılarının sayısını daha da azalttı, böylece uygulamamı hızlandırdı. İşte profilerimin kodu ve sonuç.

#!/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 sonucu:

343080 function calls (343068 primitive calls) in 2.580 seconds

Ve testRequest2 sonucu:

3129 function calls (3105 primitive calls) in 0.024 seconds

13
Bunun nedeni, chunk_sizevarsayılan olarak 1 olan parametreyi belirtmemenizdir , bu nedenle iter_contentbir seferde sonuç akışı 1 baytı üzerinden yinelenir. Python-requests.org/tr/latest/api/… belgelerine bakın .
CadentOrange

10
Bu, tüm yanıtı önlemek isteyebileceğiniz belleğe yükler. PILBurada da kullanılacak bir şey yok , sadece with open(image_name, 'wb') as outfile: outfile.write(r.content)yeter.
Martijn Pieters

3
PILayrıca standart kütüphanede bu biraz daha az taşınabilir yapar.
jjj

2
@ZhenyiZhang iter_contentyavaş çünkü senin chunk_sizeçok küçük, 100k'ye çıkarırsan çok daha hızlı olacak.
Wang

Bu en iyi cevap. Dosyayı belleğe okumak her zaman en iyisi değildir, ancak OP belirtilen "görüntüler" anlamına gelir, bu da dosyaların genellikle 4 MB'tan az olacağı ve böylece bellek üzerinde önemsiz bir etkiye sahip olacağı anlamına gelir.
Chris Conlan

51

Bu kullanmaktan daha kolay olabilir requests. Bu, requestsHTTP şeyler yapmak için kullanılmamasını önereceğim tek zamandır .

İki astar kullanarak urllib:

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

wgetKullanımı oldukça kolay olan güzel bir Python modülü de var . Burada bulundu .

Bu, tasarımın basitliğini gösterir:

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

Zevk almak.

Düzenle:out Bir yol belirtmek için bir parametre de ekleyebilirsiniz .

>>> out_filepath = <output_filepath>    
>>> filename = wget.download(url, out=out_filepath)

Hiç wgetuğraşmadan kullandım . Kullanmanın faydalarını belirttiğiniz için teşekkürlerurllib3
h3xh4wk

1
Bu cevabın Python 2 için olduğunu unutmayın. Python 3 için yapmanız gerekenler urllib.request.urlretrieve("http://example.com", "file.ext").
Husky

1
Teşekkürler @Husky. Güncellenmiş.
Blairg23

28

Aşağıdaki kod pasajı bir dosyayı indirir.

Dosya, belirtilen url'deki dosya adıyla kaydedilir.

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 ana yol vardır:

  1. Kullanarak .content(en basit / resmi) (bkz. Zhenyi Zhang'ın cevabı ):

    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. Kullanarak .raw(bkz. Martijn Pieters'ın cevabı ):

    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.

Her iki zamanlama da gözle görülür bir fark göstermez.


2
Bir sürü cevap denedim ve 1.cevabınız ( io.BytesIOve kullanarak Image) Python 3.6'da benim için çalışan ilk cevaptı . Unutma from PIL import Image(ve pip install Pillow).
colllin

.Content ve .raw arasındaki farklar nelerdir?
foxiris

13

Görüntü ve istekleri içe aktarmak kadar kolay

from PIL import Image
import requests

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

4

İşte hala akış kullanan daha kullanıcı dostu bir cevap.

Sadece bu işlevleri tanımlayın ve arayın getImage(). URL ile aynı dosya adını kullanır ve varsayılan olarak geçerli dizine yazar, ancak her ikisi de değiştirilebilir.

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

requestBağırsaklar getImage()cevap dayanmaktadır burada ve bağırsaklarından getImageFast()cevap dayanmaktadır yukarıda .


3

Yorum yapmak için yeterli temsilcim olmadığı için bir cevap göndereceğim, ancak Blairg23 tarafından gönderilen wget ile, yol için bir çıkış parametresi de sağlayabilirsiniz.

 wget.download(url, out=path)

2

Bu, istekleri olan bir ikili dosyanın nasıl indirileceği konusunda Google aramalarında ortaya çıkan ilk yanıttır. İstekleri olan rastgele bir dosya indirmeniz gerektiğinde şunları kullanabilirsiniz:

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
Güzel! Örtük bir şeyi bile var .close(). 2019 itibariyle en iyi cevap bu.
Daniel W.

2

Ben böyle yaptım

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

Bunun gibi bir şey yapabilirsiniz:

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.