Come scaricare l'immagine usando le richieste


368

Sto cercando di scaricare e salvare un'immagine dal web usando il requestsmodulo di Python .

Ecco il codice (funzionante) che ho usato:

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

Ecco il nuovo codice (non funzionante) usando 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)

Potete aiutarmi su quale attributo dalla risposta utilizzare requests?


16
per usare r.raw devi impostare stream = True
clsung il

Risposte:


517

È possibile utilizzare l' response.rawoggetto file o scorrere la risposta.

L'uso response.rawdell'oggetto simile a un file non decodificherà, per impostazione predefinita, le risposte compresse (con GZIP o deflate). Puoi forzarlo a decomprimerlo comunque impostando l' decode_contentattributo su True(lo requestsimposta Falseper controllare la decodifica stessa). È quindi possibile utilizzare shutil.copyfileobj()per fare in modo che Python esegua lo streaming dei dati su un oggetto 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)        

Per scorrere la risposta utilizzare un ciclo; iterando in questo modo si assicura che i dati vengano decompressi in questa fase:

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)

Questo leggerà i dati in blocchi da 128 byte; se ritieni che un'altra dimensione del blocco funzioni meglio, utilizza il Response.iter_content()metodo con una dimensione del blocco personalizzata:

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)

Nota che devi aprire il file di destinazione in modalità binaria per assicurarti che python non tenti di tradurre nuove righe per te. Abbiamo anche impostato in stream=Truemodo che requestsnon scarichi prima l'intera immagine in memoria.


2
Con l'aiuto della tua risposta sono riuscito a trovare i dati nel file di testo, i passaggi che ho usato sono r2 = requests.post(r.url, data); print r2.content. Ma ora voglio anche sapere filename. è un loro modo pulito? - attualmente ho trovato il nome del file nell'intestazione - r2.headers['content-disposition'] che mi dà l'output come: 'attachment; filename=DELS36532G290115.csi' sto analizzando questa stringa per il nome del file ... è il loro modo più pulito?
Grijesh Chauhan,

6
@GrijeshChauhan: sì, l' content-dispositionintestazione è la strada da percorrere qui; usare cgi.parse_header()per analizzarlo e ottenere i parametri; params = cgi.parse_header(r2.headers['content-disposition'])[1]allora params['filename'].
Martijn Pieters

1
Per ottenere i pezzi byte di default 128, è necessario iterare la requests.Responsestessa : for chunk in r: .... La chiamata iter_content()senza chunk_sizetestamento ripeterà blocchi di 1 byte .
dk

@dtk: grazie, aggiornerò la risposta. L'iterazione è cambiata dopo che ho pubblicato la mia risposta .
Martijn Pieters

1
@KumZ due motivi: response.oknon è mai stato documentato e produce true per qualsiasi stato 1xx, 2xx o 3xx, ma solo una risposta 200 ha un corpo di risposta.
Martijn Pieters

232

Ottieni un oggetto simile a un file dalla richiesta e copialo in un file. Ciò eviterà anche di leggere tutto in memoria contemporaneamente.

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
Grazie mille per essere tornato e aver risposto a questo. Anche se l'altra risposta è
efficace

11
Vale la pena notare che pochi server sono impostati su GZIP per le loro immagini perché le immagini hanno già una propria compressione. È controproducente, spreca i cicli della CPU con pochi benefici. Quindi, mentre questo può essere un problema con il contenuto del testo, in particolare con le immagini non lo è.
phette23,

3
esiste un modo per accedere al nome file originale
mahes

@ phette23 Vale anche la pena notare che Google PageSpeed ​​riporta e lo fa per impostazione predefinita.
Wernight,

8
Dovrebbe essere impostato r.raw.decode_content = Trueprima shutil.copyfileobj(response.raw, out_file)perché by default, decode compressed responses (with GZIP or deflate), quindi otterrai un'immagine a file zero.
Simin Jie,

166

Che ne dici di questo, una soluzione rapida.

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
cosa intendi con! f = open("/Users/apple/Desktop/sample.jpg", 'wb')cosa intendi con questo percorso !? voglio scaricare l'immagine
sorridi il

3
Ciò apre un descrittore di file nel percorso specificato in cui è possibile scrivere il file di immagine.
kiranbkrishna,

@AndrewGlazkov Penso che sarebbe più Pythonic da usareif response.ok:
EndermanAPM

5
response.ok è vero per qualsiasi stato 1xx, 2xx o 3xx, ma solo una 200 risposta ha un corpo di risposta come @Martijn Pieters menzionato nei commenti sopra
annndrey,

75

Ho lo stesso bisogno di scaricare immagini usando le richieste. Ho provato prima la risposta di Martijn Pieters, e funziona bene. Ma quando ho creato un profilo su questa semplice funzione, ho scoperto che utilizza così tante chiamate di funzione rispetto a urllib e urllib2.

Ho quindi provato il modo raccomandato dall'autore del modulo di richiesta:

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

Ciò ha notevolmente ridotto il numero di chiamate di funzione, accelerando così la mia applicazione. Ecco il codice del mio profiler e il risultato.

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

Il risultato per testRequest:

343080 function calls (343068 primitive calls) in 2.580 seconds

E il risultato per testRequest2:

3129 function calls (3105 primitive calls) in 0.024 seconds

13
Questo perché non hai specificato il chunk_sizeparametro predefinito 1, quindi iter_contentsta iterando sul flusso di risultati 1 byte alla volta. Vedi la documentazione python-requests.org/en/latest/api/… .
CadentOrange

10
Questo carica anche l'intera risposta in memoria, che potresti voler evitare. Non c'è nemmeno da usare PILqui, with open(image_name, 'wb') as outfile: outfile.write(r.content)è sufficiente.
Martijn Pieters

3
PILinoltre non è nella libreria standard rendendo questo un po 'meno portatile.
jjj

2
@ZhenyiZhang iter_contentè lento perché il tuo chunk_sizeè troppo piccolo, se lo aumenti a 100k sarà molto più veloce.
Wang

Questa è la risposta migliore Non è sempre meglio leggere il file in memoria, ma le "immagini" specificate dall'OP significano che i file saranno generalmente inferiori a 4 MB, con un impatto insignificante sulla memoria.
Chris Conlan,

52

Questo potrebbe essere più semplice dell'uso requests. Questa è l'unica volta che suggerirò mai di non usare requestsper fare cose HTTP.

Due fodere usando urllib:

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

C'è anche un bel modulo Python chiamato wgetche è abbastanza facile da usare. Trovato qui .

Ciò dimostra la semplicità del design:

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

Godere.

Modifica: è inoltre possibile aggiungere un outparametro per specificare un percorso.

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

Ho usato wgetsenza problemi. Grazie per aver dichiarato i vantaggi dell'utilizzourllib3
h3xh4wk il

1
Nota che questa risposta è per Python 2. Per Python 3 devi farlo urllib.request.urlretrieve("http://example.com", "file.ext").
Husky,

1
Grazie @Husky. Aggiornato.
Blairg23

28

Il frammento di codice seguente scarica un file.

Il file viene salvato con il suo nome file come nell'URL specificato.

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

Ci sono 2 modi principali:

  1. Uso .content(più semplice / ufficiale) (vedi la risposta di 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. Usando .raw(vedi la risposta di 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.

Il tempismo di entrambi non mostra alcuna differenza evidente.


2
Ho provato un sacco di risposte e la tua 1.risposta (usando io.BytesIOe Image) è stata la prima che ha funzionato per me su Python 3.6. Non dimenticare from PIL import Image(e pip install Pillow).
Colllin,

Cosa c'è di diverso tra .content e .raw?
Foxiris,

13

Facile come importare immagini e richieste

from PIL import Image
import requests

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

4

Ecco una risposta più user-friendly che utilizza ancora lo streaming.

Basta definire queste funzioni e chiamare getImage(). Utilizzerà lo stesso nome file dell'URL e scriverà nella directory corrente per impostazione predefinita, ma entrambi possono essere modificati.

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

Le requestviscere di getImage()si basano sulla risposta qui e le viscere di getImageFast()si basano sulla risposta sopra .


3

Pubblicherò una risposta poiché non ho abbastanza rappresentante per fare un commento, ma con wget come pubblicato da Blairg23, puoi anche fornire un parametro out per il percorso.

 wget.download(url, out=path)

2

Questa è la prima risposta che arriva per le ricerche su Google su come scaricare un file binario con richieste. Nel caso in cui sia necessario scaricare un file arbitrario con richieste, è possibile utilizzare:

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
Bello! Ha anche un implicito .close(). Questa è la migliore risposta a partire dal 2019, credo.
Daniel W.

2

È così che l'ho fatto

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

Puoi fare qualcosa del genere:

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.