鍍金池/ 教程/ Python/ 下載項(xiàng)目圖片
Benchmarking
命令行工具(Command line tools)
下載器中間件(Downloader Middleware)
信號(hào)(Signals)
Telnet 終端(Telnet Console)
初窺 Scrapy
數(shù)據(jù)收集(Stats Collection)
Scrapyd
通用爬蟲(Broad Crawls)
Item Loaders
試驗(yàn)階段特性
Scrapy 入門教程
自動(dòng)限速(AutoThrottle)擴(kuò)展
Settings
Scrapy 終端(Scrapy shell)
下載項(xiàng)目圖片
DjangoItem
調(diào)試(Debugging)Spiders
選擇器(Selectors)
Feed exports
Spiders Contracts
借助 Firefox 來爬取
Logging
Spiders
Ubuntu 軟件包
實(shí)踐經(jīng)驗(yàn)(Common Practices)
安裝指南
Item Exporters
擴(kuò)展(Extensions)
Items
Spider 中間件(Middleware)
異常(Exceptions)
例子
發(fā)送 email
架構(gòu)概覽
常見問題(FAQ)
Jobs:暫停,恢復(fù)爬蟲
核心 API
使用 Firebug 進(jìn)行爬取
Item Pipeline
Link Extractors
Web Service
調(diào)試內(nèi)存溢出

下載項(xiàng)目圖片

Scrapy 提供了一個(gè) Item Pipeline,來下載屬于某個(gè)特定項(xiàng)目的圖片,比如,當(dāng)你抓取產(chǎn)品時(shí),也想把它們的圖片下載到本地。

這條管道,被稱作圖片管道,在 ImagesPipeline 類中實(shí)現(xiàn),提供了一個(gè)方便并具有額外特性的方法,來下載并本地存儲(chǔ)圖片:

  • 將所有下載的圖片轉(zhuǎn)換成通用的格式(JPG)和模式(RGB)
  • 避免重新下載最近已經(jīng)下載過的圖片
  • 縮略圖生成
  • 檢測(cè)圖像的寬/高,確保它們滿足最小限制

這個(gè)管道也會(huì)為那些當(dāng)前安排好要下載的圖片保留一個(gè)內(nèi)部隊(duì)列,并將那些到達(dá)的包含相同圖片的項(xiàng)目連接到那個(gè)隊(duì)列中。 這可以避免多次下載幾個(gè)項(xiàng)目共享的同一個(gè)圖片。

Pillow 是用來生成縮略圖,并將圖片歸一化為 JPEG/RGB 格式,因此為了使用圖片管道,你需要安裝這個(gè)庫(kù)。Python Imaging Library(PIL) 在大多數(shù)情況下是有效的,但眾所周知,在一些設(shè)置里會(huì)出現(xiàn)問題,因此我們推薦使用 Pillow 而不是 PIL

使用圖片管道

當(dāng)使用 ImagesPipeline,典型的工作流程如下所示:

  1. 在一個(gè)爬蟲里,你抓取一個(gè)項(xiàng)目,把其中圖片的 URL 放入 image_urls 組內(nèi)。
  2. 項(xiàng)目從爬蟲內(nèi)返回,進(jìn)入項(xiàng)目管道。
  3. 當(dāng)項(xiàng)目進(jìn)入 ImagesPipeline,image_urls 組內(nèi)的 URLs 將被 Scrapy 的調(diào)度器和下載器(這意味著調(diào)度器和下載器的中間件可以復(fù)用)安排下載,當(dāng)優(yōu)先級(jí)更高,會(huì)在其他頁(yè)面被抓取前處理。項(xiàng)目會(huì)在這個(gè)特定的管道階段保持“l(fā)ocker”的狀態(tài),直到完成圖片的下載(或者由于某些原因未完成下載)。
  4. 當(dāng)圖片下載完,另一個(gè)組(images)將被更新到結(jié)構(gòu)中。這個(gè)組將包含一個(gè)字典列表,其中包括下載圖片的信息,比如下載路徑、源抓取地址(從 image_urls 組獲得)和圖片的校驗(yàn)碼。images 列表中的圖片順序?qū)⒑驮?image_urls 組保持一致。如果某個(gè)圖片下載失敗,將會(huì)記錄下錯(cuò)誤信息,圖片也不會(huì)出現(xiàn)在 images 組中。

使用樣例

為了使用圖片管道,你僅需要啟動(dòng)它并用 image_urlsimages 定義一個(gè)項(xiàng)目:

import scrapy

class MyItem(scrapy.Item):

    # ... other item fields ...
    image_urls = scrapy.Field()
    images = scrapy.Field()

如果你需要更加復(fù)雜的功能,想重寫定制圖片管道行為,參見實(shí)現(xiàn)定制圖片管道

開啟你的圖片管道

為了開啟你的圖片管道,你首先需要在項(xiàng)目中添加它 ITEM_PIPELINES setting:

ITEM_PIPELINES = {'scrapy.contrib.pipeline.images.ImagesPipeline': 1}

并將 IMAGES_STORE 設(shè)置為一個(gè)有效的文件夾,用來存儲(chǔ)下載的圖片。否則管道將保持禁用狀態(tài),即使你在 ITEM_PIPELINES 設(shè)置中添加了它。

比如:

IMAGES_STORE = '/path/to/valid/dir'

圖片存儲(chǔ)

文件系統(tǒng)是當(dāng)前官方唯一支持的存儲(chǔ)系統(tǒng),但也支持(非公開的) Amazon S3

文件系統(tǒng)存儲(chǔ)

圖片存儲(chǔ)在文件中(一個(gè)圖片一個(gè)文件),并使用它們 URL 的 SHA1 hash 作為文件名。

比如,對(duì)下面的圖片 URL:

http://www.example.com/image.jpg

它的 SHA1 hash 值為:

3afec3b4765f8f0a07b78f98c07b83f013567a0a

將被下載并存為下面的文件:

<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg

其中:

  • <IMAGES_STORE>是定義在 IMAGES_STORE 設(shè)置里的文件夾
  • full 是用來區(qū)分圖片和縮略圖(如果使用的話)的一個(gè)子文件夾。詳情參見縮略圖生成

額外的特性

圖片失效

圖像管道避免下載最近已經(jīng)下載的圖片。使用 IMAGES_EXPIRES 設(shè)置可以調(diào)整失效期限,可以用天數(shù)來指定:

# 90 天的圖片失效期限
IMAGES_EXPIRES = 90

縮略圖生成

圖片管道可以自動(dòng)創(chuàng)建下載圖片的縮略圖。

為了使用這個(gè)特性,你需要設(shè)置 IMAGES_THUMBS 字典,其關(guān)鍵字為縮略圖名字,值為它們的大小尺寸。

比如:

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}

當(dāng)你使用這個(gè)特性時(shí),圖片管道將使用下面的格式來創(chuàng)建各個(gè)特定尺寸的縮略圖:

<IMAGES_STORE>/thumbs/<size_name>/<image_id>.jpg

其中:

  • <size_name>IMAGES_THUMBS字典關(guān)鍵字(small,big,等)
  • <image_id>是圖像 url 的 SHA1 hash

例如使用 smallbig 縮略圖名字的圖片文件:

<IMAGES_STORE>/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg

第一個(gè)是從網(wǎng)站下載的完整圖片。

濾出小圖片

你可以丟掉那些過小的圖片,只需在:setting:*IMAGES_MIN_HEIGHT*IMAGES_MIN_WIDTH 設(shè)置中指定最小允許的尺寸。

比如:

IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

注意:這些尺寸一點(diǎn)也不影響縮略圖的生成。

默認(rèn)情況下,沒有尺寸限制,因此所有圖片都將處理。

實(shí)現(xiàn)定制圖片管道

下面是你可以在定制的圖片管道里重寫的方法:

class scrapy.contrib.pipeline.images.ImagesPipeline

get_media_requests(item, info)

在工作流程中可以看到,管道會(huì)得到圖片的 URL 并從項(xiàng)目中下載。為了這么做,你需要重寫 get_media_requests()方法,并對(duì)各個(gè)圖片 URL 返回一個(gè) Request:

def get_media_requests(self, item, info):
    for image_url in item['image_urls']:
        yield scrapy.Request(image_url)

這些請(qǐng)求將被管道處理,當(dāng)它們完成下載后,結(jié)果將以 2-元素的元組列表形式傳送到 item_completed()方法:

  • success 是一個(gè)布爾值,當(dāng)圖片成功下載時(shí)為 True,因?yàn)槟硞€(gè)原因下載失敗為False
  • image_info_or_error 是一個(gè)包含下列關(guān)鍵字的字典(如果成功為 True)或者出問題時(shí)為 Twisted Failure。
    • url - 圖片下載的 url。這是從 get_media_requests()方法返回請(qǐng)求的 url。
    • path - 圖片存儲(chǔ)的路徑(類似 IMAGES_STORE
    • checksum - 圖片內(nèi)容的 MD5 hash item_completed()接收的元組列表需要保證與 get_media_requests()方法返回請(qǐng)求的順序相一致。下面是 results 參數(shù)的一個(gè)典型值:
[(True,
  {'checksum': '2b00042f7481c7b056c4b410d28f33cf',
   'path': 'full/7d97e98f8af710c7e7fe703abc8f639e0ee507c4.jpg',
   'url': 'http://www.example.com/images/product1.jpg'}),
 (True,
  {'checksum': 'b9628c4ab9b595f72f280b90c4fd093d',
   'path': 'full/1ca5879492b8fd606df1964ea3c1e2f4520f076f.jpg',
   'url': 'http://www.example.com/images/product2.jpg'}),
 (False,
  Failure(...))]

默認(rèn) get_media_requests()方法返回 None,這意味著項(xiàng)目中沒有圖片可下載。

item_completed(results, items, info)

當(dāng)一個(gè)單獨(dú)項(xiàng)目中的所有圖片請(qǐng)求完成時(shí)(要么完成下載,要么因?yàn)槟撤N原因下載失敗), ImagesPipeline.item_completed()方法將被調(diào)用。

item_completed()方法需要返回一個(gè)輸出,其將被送到隨后的項(xiàng)目管道階段,因此你需要返回(或者丟棄)項(xiàng)目,如你在任意管道里所做的一樣。

這里是一個(gè) item_completed() 方法的例子,其中我們將下載的圖片路徑(傳入到 results 中)存儲(chǔ)到 image_paths 項(xiàng)目組中,如果其中沒有圖片,我們將丟棄項(xiàng)目:

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

默認(rèn)情況下,item_completed()方法返回項(xiàng)目。

定制圖片管道的例子

下面是一個(gè)圖片管道的完整例子,其方法如上所示:

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