Media Pipeline
Scrapy為下載item中包含的文件(比如在爬取到產(chǎn)品時(shí)资柔,同時(shí)也想保存對(duì)應(yīng)的圖片)提供了一個(gè)可重用的 item pipelines . 這些pipeline有些共同的方法和結(jié)構(gòu)(稱(chēng)之為media pipeline)狼纬。我們可以使用FilesPipeline和Images Pipeline來(lái)保存文件和圖片宫峦,他們有以下的一些特點(diǎn):
-
Files Pipeline
- 避免重新下載最近已經(jīng)下載過(guò)的數(shù)據(jù)
- 指定存儲(chǔ)路徑
FilesPipeline的典型工作流程如下:
- 在一個(gè)爬蟲(chóng)里,你抓取一個(gè)項(xiàng)目砚殿,把其中圖片的URL放入 file_urls 組內(nèi)戏售。
- 項(xiàng)目從爬蟲(chóng)內(nèi)返回,進(jìn)入項(xiàng)目管道贷帮。
- 當(dāng)項(xiàng)目進(jìn)入 FilesPipeline,file_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)蔬啡,直到完成文件的下載(或者由于某些原因未完成下載)。
- 當(dāng)文件下載完后镀虐,另一個(gè)字段(files)將被更新到結(jié)構(gòu)中箱蟆。這個(gè)組將包含一個(gè)字典列表,其中包括下載文件的信息刮便,比如下載路徑空猜、源抓取地址(從 file_urls 組獲得)和圖片的校驗(yàn)碼(checksum)。 files 列表中的文件順序?qū)⒑驮?file_urls 組保持一致恨旱。如果某個(gè)圖片下載失敗辈毯,將會(huì)記錄下錯(cuò)誤信息,圖片也不會(huì)出現(xiàn)在 files 組中搜贤。
-
Images Pipeline
- 避免重新下載最近已經(jīng)下載過(guò)的數(shù)據(jù)
- 指定存儲(chǔ)路徑
- 將所有下載的圖片轉(zhuǎn)換成通用的格式(JPG)和模式(RGB)
- 縮略圖生成
- 檢測(cè)圖像的寬/高谆沃,確保它們滿(mǎn)足最小限制
和FilesPipeline類(lèi)似,除了默認(rèn)的字段名不同仪芒,image_urls保存圖片URL地址唁影,images保存下載后的圖片信息耕陷。當(dāng)然,它還提供了一些拓展功能据沈,比如圖片的縮略圖哟沫,過(guò)濾圖片的尺寸。
注意:你需要安裝Pillow 庫(kù)來(lái)實(shí)現(xiàn)以上的拓展功能锌介。
啟用Media Pipeline
要想使用media pipeline嗜诀,你需要在設(shè)置添加一些必要的信息。
# 同時(shí)啟用圖片和文件管道
ITEM_PIPELINES = {
'scrapy.pipelines.images.ImagesPipeline': 1,
'scrapy.pipelines.files.FilesPipeline': 2,
}
FILES_STORE = 'D:' # 文件存儲(chǔ)路徑
IMAGES_STORE = 'D' # 圖片存儲(chǔ)路徑
# 避免下載最近90天已經(jīng)下載過(guò)的文件內(nèi)容
FILES_EXPIRES = 90
# 避免下載最近90天已經(jīng)下載過(guò)的圖像內(nèi)容
IMAGES_EXPIRES = 30
# 設(shè)置圖片縮略圖
IMAGES_THUMBS = {
'small': (50, 50),
'big': (250, 250),
}
# 圖片過(guò)濾器孔祸,最小高度和寬度裹虫,低于此尺寸不下載
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110
你可能會(huì)好奇文件的命名,在當(dāng)你啟用media pipeline以后融击,
它的默認(rèn)命名方式是這樣的筑公,文件以它們URL的 SHA1 hash 作為文件名。
例如尊浪,
對(duì)下面的圖片URL:http://www.example.com/image.jpg
,
其SHA1 hash 值為:3afec3b4765f8f0a07b78f98c07b83f013567a0a
將被下載并存為下面的文件:<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg
其中匣屡,<IMAGES_STORE> 是定義在 IMAGES_STORE 設(shè)置里的文件夾,我們?cè)O(shè)置的是D盤(pán)拇涤,full 是用來(lái)區(qū)分圖片和縮略圖(如果使用的話(huà))的一個(gè)子文件夾捣作,這個(gè)文件夾scrapy會(huì)自動(dòng)生成。
擴(kuò)展Media Pipeline
下面我們以ImagesPipeline為例來(lái)自定義ImagesPipeline鹅士,需要重寫(xiě)以下兩個(gè)方法:
-
get_media_requests(item, info)
在工作流程中可以看到券躁,管道會(huì)得到圖片的URL并從項(xiàng)目中下載。為了這么做掉盅,你需要重寫(xiě) 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() 方法: 每個(gè)元組包含 (success, file_info_or_error):
success 是一個(gè)布爾值慢哈,當(dāng)圖片成功下載時(shí)為 True ,因?yàn)槟硞€(gè)原因下載失敗為False
-
file_info_or_error 是一個(gè)包含下列關(guān)鍵字的字典(如果成功為 True )或者出問(wèn)題時(shí)為 Twisted Failure 永票。
url - 文件下載的url卵贱。這是從 get_media_requests() 方法返回請(qǐng)求的url。
path - 圖片存儲(chǔ)的路徑(類(lèi)似 IMAGES_STORE)
checksum - 圖片內(nèi)容的 MD5 hash
item_completed() 接收的元組列表需要保證與 get_media_requests() 方法返回請(qǐng)求的順序相一致侣集。下面是 results 參數(shù)的一個(gè)典型值:[(True, {'checksum': '2b00042f7481c7b056c4b410d28f33cf', 'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg', 'url': 'http://www.example.com/files/product1.jpg'}), (False, Failure(...))]
該方法 必須返回每一個(gè)圖片的URL键俱。
item_completed(results, items, info)
當(dāng)一個(gè)單獨(dú)項(xiàng)目中的所有圖片請(qǐng)求完成時(shí),例如,item里面一共有10個(gè)URL世分,那么當(dāng)這10個(gè)URL全部下載完成以后编振,ImagesPipeline.item_completed() 方法將被調(diào)用。默認(rèn)情況下罚攀, item_completed() 方法返回item党觅。
使用ImagesPipeline下載圖片
下面我們用上面學(xué)習(xí)到的知識(shí)來(lái)下載一些圖片雌澄。
我們以http://jandan.net/ooxx為例,把頁(yè)面上的圖片下載下來(lái)杯瞻,并產(chǎn)生縮略圖
我們新建一個(gè)項(xiàng)目镐牺,名為jiandan,各個(gè)文件內(nèi)容如下魁莉。
item.py
import scrapy
class JiandanItem(scrapy.Item):
image_urls = scrapy.Field()#圖片的鏈接
images = scrapy.Field()
jiandan_spider.py
import scrapy
from jiandan.items import JiandanItem
class jiandanSpider(scrapy.Spider):
name = 'jiandan'
start_urls = ["http://jandan.net/ooxx"]
def parse(self, response):
item = JiandanItem()
item['image_urls'] = response.xpath('//img//@src').extract() #提取圖片鏈接
yield item
settings.py
BOT_NAME = 'jiandan'
SPIDER_MODULES = ['jiandan.spiders']
NEWSPIDER_MODULE = 'jiandan.spiders'
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
}
ITEM_PIPELINES = {
'jiandan.pipelines.JiandanPipeline':1,
}
IMAGES_STORE='H:\\jiandan'
IMAGES_THUMBS = {
'small': (50, 50),
'big': (200, 200),
}
pipelinse.py
import scrapy
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline #內(nèi)置的圖片管道
class JiandanPipeline(ImagesPipeline):#繼承ImagesPipeline這個(gè)類(lèi)
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
image_url = "http://" + image_url
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")
return item
運(yùn)行這個(gè)spider睬涧,你會(huì)發(fā)現(xiàn),圖片已經(jīng)下載好了旗唁,如下圖所示畦浓。
圖片內(nèi)容你可以自己慢慢看。