mirror of https://github.com/scrapy/scrapy.git
279 lines
9.4 KiB
ReStructuredText
279 lines
9.4 KiB
ReStructuredText
.. _topics-images:
|
|
|
|
=======================
|
|
Downloading Item Images
|
|
=======================
|
|
|
|
.. currentmodule:: scrapy.contrib.pipeline.images
|
|
|
|
Scrapy provides an :doc:`item pipeline </topics/item-pipeline>` for downloading
|
|
images attached to a particular item. For example, when you scrape products and
|
|
also want to download their images locally.
|
|
|
|
This pipeline, called the Images Pipeline and implemented in the
|
|
:class:`ImagesPipeline` class, provides a convenient way for
|
|
downloading and storing images locally with some additional features:
|
|
|
|
* Convert all downloaded images to a common format (JPG) and mode (RGB)
|
|
* Avoid re-downloading images which were downloaded recently
|
|
* Thumbnail generation
|
|
* Check images width/height to make sure they meet a minimum constraint
|
|
|
|
This pipeline also keeps an internal queue of those images which are currently
|
|
being scheduled for download, and connects those items that arrive containing
|
|
the same image, to that queue. This avoids downloading the same image more than
|
|
once when it's shared by several items.
|
|
|
|
The `Python Imaging Library`_ is used for thumbnailing and normalizing images
|
|
to JPEG/RGB format, so you need to install that library in order to use the
|
|
images pipeline.
|
|
|
|
.. _Python Imaging Library: http://www.pythonware.com/products/pil/
|
|
|
|
Using the Images Pipeline
|
|
=========================
|
|
|
|
The typical workflow, when using the :class:`ImagesPipeline` goes like
|
|
this:
|
|
|
|
1. In a Spider, you scrape an item and put the URLs of its images into a
|
|
pre-defined field, for example ``image_urls``.
|
|
|
|
2. The item is returned from the spider and goes to the item pipeline.
|
|
|
|
3. When the item reaches the :class:`ImagesPipeline`, the URLs in the
|
|
``image_urls`` attribute are scheduled for download using the standard
|
|
Scrapy scheduler and downloader (which means the scheduler and downloader
|
|
middlewares are reused), but higher priority to process them before other
|
|
pages to scrape. The item remains "locked" at that particular pipeline stage
|
|
until the images have finish downloading (or fail for some reason).
|
|
|
|
4. When the images finish downloading (or fail for some reason) the images gets
|
|
another field populated with the path of the images downloaded, for example,
|
|
``image_paths``. This attribute is a list of dictionaries containing
|
|
information about the image downloaded, such as the downloaded path, and the
|
|
original scraped url. This images in the list of the ``image_paths`` field
|
|
would retain the same order of the original ``image_urls`` field, which is
|
|
useful if you decide to use the first image in the list as the primary
|
|
image.
|
|
|
|
.. setting:: IMAGES_STORE
|
|
|
|
The first thing we need to do is tell the pipeline where to store the
|
|
downloaded images, through the :setting:`IMAGES_STORE` setting::
|
|
|
|
IMAGES_STORE = '/path/to/valid/dir'
|
|
|
|
Then, as seen on the workflow, the pipeline will get the URLs of the images to
|
|
download from the item. In order to do this, you must override the
|
|
:meth:`~ImagesPipeline.get_media_requests` method and return a Request for each
|
|
image URL::
|
|
|
|
def get_media_requests(self, item, info):
|
|
for image_url in item['image_urls']:
|
|
yield Request(image_url)
|
|
|
|
Those requests will be processed by the pipeline and, when they have finished
|
|
downloading, the results will be sent to the
|
|
:meth:`~ImagesPipeline.item_completed` method, as a list of 2-element tuples.
|
|
Each tuple will contain ``(success, image_info_or_failure)`` where:
|
|
|
|
* ``success`` is a boolean which is ``True`` if the image was downloading
|
|
successfully or ``False`` if it failed for some reason
|
|
|
|
* ``image_info_or_error`` is a dict containing the following keys (if success
|
|
is ``True``) or a `Twisted Failure`_ if there was a problem.
|
|
|
|
* ``url`` - the url where the image was downloaded from. This is the url of
|
|
the request returned from the :meth:`~ImagesPipeline.get_media_requests`
|
|
method.
|
|
|
|
* ``path`` - the path (relative to :setting:`IMAGES_STORE`) where the image
|
|
was stored
|
|
|
|
* ``checksum`` - a `MD5`_ hash of the image contents
|
|
|
|
.. _Twisted Failure: http://twistedmatrix.com/documents/8.2.0/api/twisted.python.failure.Failure.html
|
|
.. _MD5: http://en.wikipedia.org/wiki/MD5
|
|
|
|
The list of tuples received by :meth:`~ImagesPipeline.item_completed` is
|
|
guaranteed to retain the same order of the requests returned from the
|
|
:meth:`~ImagesPipeline.get_media_requests` method.
|
|
|
|
Here's a typical an example value of ``results`` argument::
|
|
|
|
[(True,
|
|
{'checksum': '2b00042f7481c7b056c4b410d28f33cf',
|
|
'path': '7d97e98f8af710c7e7fe703abc8f639e0ee507c4.jpg',
|
|
'url': 'http://www.example.com/images/product1.jpg'}),
|
|
(True,
|
|
{'checksum': 'b9628c4ab9b595f72f280b90c4fd093d',
|
|
'path': '1ca5879492b8fd606df1964ea3c1e2f4520f076f',
|
|
'url': 'http://www.example.com/images/product2.jpg'}),
|
|
(False,
|
|
Failure(...))]
|
|
|
|
The :meth:`~ImagesPipeline.item_completed` method must return the output that
|
|
will be sent to further item pipeline stages, so you must return (or drop) the
|
|
item, as you would in any pipeline.
|
|
|
|
Here is an example of :meth:`~ImagesPipeline.item_completed` method where we
|
|
store the downloaded image paths (passed in results) in the ``image_paths``
|
|
item field, and we drop the item if it doesn't contain any images::
|
|
|
|
from scrapy.core.exceptions import DropItem
|
|
|
|
def item_completed(self, results, item, info):
|
|
image_paths = [info['path'] for success, info in results if success]
|
|
if not image_paths:
|
|
raise DropItem("Item contains no images")
|
|
item['image_paths'] = image_paths
|
|
return item
|
|
|
|
So, the complete example of our pipeline would look like this::
|
|
|
|
from scrapy.contrib.pipeline.images import ImagesPipeline
|
|
from scrapy.core.exceptions import DropItem
|
|
|
|
class MyImagesPipeline(ImagesPipeline):
|
|
|
|
def get_media_requests(self, item, info):
|
|
for image_url in item['image_urls']:
|
|
yield Request(image_url)
|
|
|
|
def item_completed(self, results, item, info):
|
|
image_paths = [info['path'] for success, info in results if success]
|
|
if not image_paths:
|
|
raise DropItem("Item contains no images")
|
|
item['image_paths'] = image_paths
|
|
return item
|
|
|
|
.. _topics-images-expiration:
|
|
|
|
Image expiration
|
|
----------------
|
|
|
|
.. setting:: IMAGES_EXPIRES
|
|
|
|
The Image Pipeline avoids downloading images that were downloaded recently. To
|
|
adjust this retention delay use the :setting:`IMAGES_EXPIRES` setting, which
|
|
specifies the delay in number of days::
|
|
|
|
# 90 days of delay for image expiration
|
|
IMAGES_EXPIRES = 90
|
|
|
|
.. _topics-images-thumbnails:
|
|
|
|
Thumbnail generation
|
|
--------------------
|
|
|
|
The Images Pipeline can automatically create thumbnails of the downloaded
|
|
images.
|
|
|
|
In order use this feature you must set the :attr:`~ImagesPipeline.THUMBS`
|
|
to a tuple of ``(size_name, (width, height))`` tuples.
|
|
|
|
Here are some examples examples.
|
|
|
|
Using numeric names::
|
|
|
|
THUMBS = (
|
|
('50', (50, 50)),
|
|
('110', (110, 110)),
|
|
)
|
|
|
|
Using textual names::
|
|
|
|
THUMBS = (
|
|
('small', (50, 50)),
|
|
('big', (270, 270)),
|
|
)
|
|
|
|
When you use this feature, the Images Pipeline will create thumbnails of the
|
|
each specified size with this format::
|
|
|
|
IMAGES_STORE/thumbs/<image_id>/<size_name>.jpg
|
|
|
|
Where:
|
|
|
|
* ``<image_id>`` is the `SHA1 hash`_ of the image url
|
|
* ``<size_name>`` is the one specified in the ``THUMBS`` attribute
|
|
|
|
.. _SHA1 hash: http://en.wikipedia.org/wiki/SHA_hash_functions
|
|
|
|
Example with using ``50`` and ``110`` thumbnail names::
|
|
|
|
IMAGES_STORE/thumbs/63bbfea82b8880ed33cdb762aa11fab722a90a24/50.jpg
|
|
IMAGES_STORE/thumbs/63bbfea82b8880ed33cdb762aa11fab722a90a24/110.jpg
|
|
|
|
Example with using ``small`` and ``big`` thumbnail names::
|
|
|
|
IMAGES_STORE/thumbs/63bbfea82b8880ed33cdb762aa11fab722a90a24/small.jpg
|
|
IMAGES_STORE/thumbs/63bbfea82b8880ed33cdb762aa11fab722a90a24/big.jpg
|
|
|
|
.. _topics-images-size:
|
|
|
|
Checking image size
|
|
-------------------
|
|
|
|
.. setting:: IMAGES_MIN_HEIGHT
|
|
|
|
.. setting:: IMAGES_MIN_WIDTH
|
|
|
|
You can drop images which are too small, by specifying the minimum allowed size
|
|
in the :setting:`IMAGES_MIN_HEIGHT` and :setting:`IMAGES_MIN_WIDTH` settings.
|
|
|
|
For example::
|
|
|
|
IMAGES_MIN_HEIGHT = 110
|
|
IMAGES_MIN_WIDTH = 110
|
|
|
|
|
|
.. _ref-images:
|
|
|
|
API Reference
|
|
=============
|
|
|
|
.. module:: scrapy.contrib.pipeline.images
|
|
:synopsis: Images Pipeline
|
|
|
|
ImagesPipeline
|
|
--------------
|
|
|
|
.. class:: ImagesPipeline
|
|
|
|
A pipeline to download images attached to items, for example product images.
|
|
|
|
To enable this pipeline you must set :setting:`IMAGES_STORE` to a valid
|
|
directory that will be used for storing the downloaded images. Otherwise the
|
|
pipeline will remain disabled, even if you include it in the
|
|
:setting:`ITEM_PIPELINES` setting.
|
|
|
|
.. method:: get_media_requests(item, info)
|
|
|
|
Return a list of Request objects to download images for this item.
|
|
|
|
Must return ``None`` or an iterable.
|
|
|
|
By default it returns ``None`` which means there are no images to
|
|
download for the item.
|
|
|
|
.. method:: item_completed(results, item, info)
|
|
|
|
Method called when all image requests for a single item have been
|
|
downloaded (or failed).
|
|
|
|
The output of this method is used as the output of the Image Pipeline
|
|
stage.
|
|
|
|
This method must returns the item itself or raise a
|
|
:exc:`~scrapy.core.exceptions.DropItem` exception.
|
|
|
|
By default, it returns the item.
|
|
|
|
.. attribute:: THUMBS
|
|
|
|
List of thumbnails to generate for the images. See
|
|
:ref:`topics-images-thumbnails`.
|
|
|