下载项目图片
Scrapy提供了一个 item pipeline ,来下载属于某个特定项目的图片,比如,当你抓取产品时,也想把它们的图片下载到本地。
这条管道,被称作图片管道,在 ImagesPipeline
类中实现,提供了一个方便并具有额外特性的方法,来下载并本地存储图片:
- 将所有下载的图片转换成通用的格式(JPG)和模式(RGB)
- 避免重新下载最近已经下载过的图片
- 缩略图生成
- 检测图像的宽/高,确保它们满足最小限制
这个管道也会为那些当前安排好要下载的图片保留一个内部队列,并将那些到达的包含相同图片的项目连接到那个队列中。 这可以避免多次下载几个项目共享的同一个图片。
使用图片管道
当使用 ImagesPipeline
,典型的工作流程如下所示:
- 在一个爬虫里,你抓取一个项目,把其中图片的URL放入
image_urls
组内。 - 项目从爬虫内返回,进入项目管道。
- 当项目进入
ImagesPipeline
,image_urls
组内的URLs将被Scrapy的调度器和下载器(这意味着调度器和下载器的中间件可以复用)安排下载,当优先级更高,会在其他页面被抓取前处理。项目会在这个特定的管道阶段保持“locker”的状态,直到完成图片的下载(或者由于某些原因未完成下载)。 - 当图片下载完,另一个组(
images
)将被更新到结构中。这个组将包含一个字典列表,其中包括下载图片的信息,比如下载路径、源抓取地址(从image_urls
组获得)和图片的校验码。images
列表中的图片顺序将和源image_urls
组保持一致。如果某个图片下载失败,将会记录下错误信息,图片也不会出现在images
组中。
使用样例
为了使用图片管道,你仅需要 启动它 并用 image_urls
和 images
定义一个项目:
import scrapy
class MyItem(scrapy.Item):
# ... other item fields ...
image_urls = scrapy.Field()
images = scrapy.Field()
如果你需要更加复杂的功能,想重写定制图片管道行为,参见 实现定制图片管道 。
开启你的图片管道
为了开启你的图片管道,你首先需要在项目中添加它 ITEM_PIPELINES
setting:
ITEM_PIPELINES = {'scrapy.contrib.pipeline.images.ImagesPipeline': 1}
并将 IMAGES_STORE
设置为一个有效的文件夹,用来存储下载的图片。否则管道将保持禁用状态,即使你在 ITEM_PIPELINES
设置中添加了它。
比如:
IMAGES_STORE = '/path/to/valid/dir'
图片存储
文件系统存储
图片存储在文件中(一个图片一个文件),并使用它们URL的 SHA1 hash 作为文件名。
比如,对下面的图片URL:
http://www.example.com/image.jpg
它的 SHA1 hash 值为:
3afec3b4765f8f0a07b78f98c07b83f013567a0a
将被下载并存为下面的文件:
<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg
实现定制图片管道
下面是你可以在定制的图片管道里重写的方法:
classscrapy.contrib.pipeline.images.ImagesPipeline
get_media_requests
(item, info**)**
在工作流程中可以看到,管道会得到图片的URL并从项目中下载。为了这么做,你需要重写 get_media_requests()
方法,并对各个图片URL返回一个Request:
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield scrapy.Request(image_url)
这些请求将被管道处理,当它们完成下载后,结果将以2-元素的元组列表形式传送到 item_completed()
方法:
默认 get_media_requests()
方法返回 None
,这意味着项目中没有图片可下载。
item_completed
(results, items**, info)**
当一个单独项目中的所有图片请求完成时(要么完成下载,要么因为某种原因下载失败), ImagesPipeline.item_completed()
方法将被调用。
item_completed()
方法需要返回一个输出,其将被送到随后的项目管道阶段,因此你需要返回(或者丢弃)项目,如你在任意管道里所做的一样。
这里是一个 item_completed()
方法的例子,其中我们将下载的图片路径(传入到results中)存储到 image_paths
项目组中,如果其中没有图片,我们将丢弃项目:
from scrapy.exceptions import DropItem
def item_completed(self, results, item, info):
image_paths = [x['path'] for ok, x in results if ok]
if not image_paths:
raise DropItem("Item contains no images")
item['image_paths'] = image_paths
return item
默认情况下, item_completed()
方法返回项目。
定制图片管道的例子
import scrapy
from scrapy.contrib.pipeline.images import ImagesPipeline
from scrapy.exceptions import DropItem
class MyImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield scrapy.Request(image_url)
def item_completed(self, results, item, info):
image_paths = [x['path'] for ok, x in results if ok]
if not image_paths:
raise DropItem("Item contains no images")
item['image_paths'] = image_paths
return item