28

I would like to find the dimensions of an image on the internet. I tried using

from PIL import Image
import urllib2 as urllib
fd = urllib.urlopen("http://a/b/c")
im = Image.open(fd)
im.size

as suggested in this answer, but I get the error message

addinfourl instance has no attribute 'seek'

I checked and objects returned by urllib2.urlopen(url) do not seem to have a seek method according to dir.

So, what do I have to do to be able to load an image from the Internet into PIL?

Community
  • 1
  • 1
murgatroid99
  • 19,007
  • 10
  • 60
  • 95

7 Answers7

44

You might consider using io.BytesIO for forward compatibility.
The StringIO and cStringIO modules do not exist in Python 3.

from PIL import Image
import urllib2 as urllib
import io

fd = urllib.urlopen("http://a/b/c")
image_file = io.BytesIO(fd.read())
im = Image.open(image_file)
Honest Abe
  • 8,430
  • 4
  • 49
  • 64
Snakes and Coffee
  • 8,747
  • 4
  • 40
  • 60
  • I would suggest using [`tempfile.SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) with a max size to avoid unintended memory exhaustion. – eestrada Jan 12 '16 at 21:44
14

Using Python requests:

from PIL import Image
from StringIO import StringIO
import requests

r = requests.get("http://a/b/c")
im = Image.open(StringIO(r.content))
im.size
Matt Parrilla
  • 3,171
  • 6
  • 35
  • 54
  • Though this answer is different than the others, it doesn't solve the actual problem any differently than jdi's answer. It still wraps the stream in `StringIO`. – murgatroid99 Aug 17 '14 at 15:56
  • 1
    This worked a treat for a Docker image that didn't have urllib2 but had requests. Thanks for posting an alternative. – charlesreid1 Oct 12 '17 at 01:29
  • 1
    Fix for ImportError: `from io import StringIO` for Python 3``` – nsde Dec 17 '21 at 15:33
10

This pull-request adds support for stream-handling native to Pillow (the friendly PIL fork) and should be available from version 2.8.0. This allows the simpler opening remote files with urllib:

from PIL import Image
import urllib2
Image.open(urllib2.urlopen(url))

...or using requests:

from PIL import Image
import requests
Image.open(requests.get(url, stream=True).raw)

As mentioned by mjpieters on the PR requests does not automatically decode gzip responses, so if you're downloading images that have been further compressed for whatever reason you must set decode_content=True on the response object before accessing .raw.

response = requests.get(url, stream=True)
response.raw.decode_content = True
image = Image.open(response.raw)
mfitzp
  • 15,275
  • 7
  • 50
  • 70
8

Using your same example, just use StringIO to wrap the buffer into a proper file-like object:

from PIL import Image
import urllib2 as urllib
from StringIO import StringIO

fd = urllib.urlopen("http://a/b/c")
im = Image.open(StringIO(fd.read()))
im.size
jdi
  • 90,542
  • 19
  • 167
  • 203
2

The urllib documentation mentions that an object returned by urlopen doesn't support seek operation.

This module provides a high-level interface for fetching data across the World Wide Web. In particular, the urlopen() function is similar to the built-in function open(), but accepts Universal Resource Locators (URLs) instead of filenames. Some restrictions apply — it can only open URLs for reading, and no seek operations are available.

However, the PIL.open function explicitly requires it.

open

Image.open(infile) => image

Image.open(infile, mode) => image

Opens and identifies the given image file. This is a lazy operation; the actual image data is not read from the file until you try to process the data (call the load method to force loading). If the mode argument is given, it must be "r".

You can use either a string (representing the filename) or a file object. In the latter case, the file object must implement read, seek, and tell methods, and be opened in binary mode.

Try using cStringIO module that converts a string into a file-like object.

from PIL import Image
import urllib2 as urllib
import cStringIO

fd = urllib.urlopen("http://a/b/c")
image_file = cStringIO.StringIO(fd.read())
im = Image.open(image_file)
im.size
Maksim Skurydzin
  • 10,301
  • 8
  • 40
  • 53
0

This answer is 4 years ago, but it's still on top in Google.In Python3, we have simple solution.

from urllib.request import urlopen
img =Image.open(urlopen('http://dl.iplaypython.com/images/banner336x280.jpg'))
new_img =img.resize((300,500),Image.ANTIALIAS)
new_img.save('url.jpg','jpeg')
刘数据
  • 25
  • 2
0

Using requests library and and get output as Bytes

import requests
import io

response = requests.get("https://i.imgur.com/ExdKOOz.png")
image_bytes = io.BytesIO(response.content)
David Buck
  • 3,752
  • 35
  • 31
  • 35