Scrapy框架之利用ImagesPipeline下載圖片

HuangQinJian發表於2017-05-29

1.ImagesPipeline簡介

Scrapy用ImagesPipeline類提供一種方便的方式來下載和儲存圖片。

特點:

  • 將下載圖片轉換成通用的JPG和RGB格式
  • 避免重複下載
  • 縮圖生成
  • 圖片大小過濾

2.ImagesPipeline工作流程

當使用圖片管道 ImagePipeline,典型的工作流程如下:

  • 在一個爬蟲裡,你抓取一個專案,把其中圖片的URL放入image_urls組內。
  • 專案從爬蟲內返回,進入專案管道。
  • 當專案進入ImagePipeline, image_urls組內的URLs將被Scrapy的排程器和下載器安排下載(這意味著排程器和中介軟體可以複用),當優先順序更高,會在其他頁面被抓取前處理. 專案會在這個特定的管道階段保持"locker"的狀態,直到完成圖片的下載(或者由於某些原因未完成下載)。
  • 當圖片下載完, 另一個組(images)將被更新到結構中,這個組將包含一個字典列表,其中包括下載圖片的資訊,比如下載路徑,源抓取地址(從image_urls組獲得)和圖片的校驗碼. images列表中的圖片順序將和源image_urls組保持一致.如果某個圖片下載失敗,將會記錄下錯誤資訊,圖片也不會出現在images組中。

3.操作過程

專案目錄結構:

Scrapy框架之利用ImagesPipeline下載圖片
這裡寫圖片描述

要想成功爬取圖片,需要經過以下幾個步驟:

(1) 在items.py中新增image_urls、images和image_paths欄位,程式碼如下:

class DoubanImgsItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    image_urls = Field()
    images = Field()
    image_paths = Field()複製程式碼

(2)在settings.py中設定條件和屬性,程式碼如下:

# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html

# ImagePipeline的自定義實現類
ITEM_PIPELINES = {
    'douban_imgs.pipelines.DoubanImgDownloadPipeline': 300,
}
#設定圖片下載路徑
IMAGES_STORE = 'D:\\doubanimgs'
# 過期天數
IMAGES_EXPIRES = 90  #90天內抓取的都不會被重抓複製程式碼

(3)在spiders/download_douban.py中書寫ImageSpider的程式碼:

# coding=utf-8
from scrapy.spiders import Spider
import re
from scrapy import Request
from ..items import DoubanImgsItem


class download_douban(Spider):
    name = 'download_douban'

    default_headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Encoding': 'gzip, deflate, sdch, br',
        'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Host': 'www.douban.com',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
    }

    def __init__(self, url='1638835355', *args, **kwargs):
        self.allowed_domains = ['douban.com']
        self.start_urls = [
            'http://www.douban.com/photos/album/%s/' % (url)]
        self.url = url
        # call the father base function

        # super(download_douban, self).__init__(*args, **kwargs)

    def start_requests(self):

        for url in self.start_urls:
            yield Request(url=url, headers=self.default_headers, callback=self.parse)

    def parse(self, response):
        list_imgs = response.xpath('//div[@class="photolst clearfix"]//img/@src').extract()
        if list_imgs:
            item = DoubanImgsItem()
            item['image_urls'] = list_imgs
            yield item複製程式碼

(4)在pipelines.py中自定義ImagePipeline程式碼:

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
from scrapy import Request
from scrapy import log


class DoubanImgsPipeline(object):
    def process_item(self, item, spider):
        return item


class DoubanImgDownloadPipeline(ImagesPipeline):
    default_headers = {
        'accept': 'image/webp,image/*,*/*;q=0.8',
        'accept-encoding': 'gzip, deflate, sdch, br',
        'accept-language': 'zh-CN,zh;q=0.8,en;q=0.6',
        'cookie': 'bid=yQdC/AzTaCw',
        'referer': 'https://www.douban.com/photos/photo/2370443040/',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
    }

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            self.default_headers['referer'] = image_url
            yield Request(image_url, headers=self.default_headers)

    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複製程式碼

在自定義ImagePipeline程式碼中,作為重要的是要過載get_media_requests(self, item, info)item_completed(self, results, item, info)這兩個函式。

  • get_media_requests(self,item, info):

ImagePipeline根據image_urls中指定的url進行爬取,可以通過get_media_requests為每個url生成一個Request。如:

for image_url in item['image_urls']:
            self.default_headers['referer'] = image_url
            yield Request(image_url, headers=self.default_headers)複製程式碼
  • item_completed(self, results, item, info):

圖片下載完畢後,處理結果會以二元組的方式返回給item_completed()函式。這個二元組定義如下:

(success, image_info_or_failure)

其中,第一個元素表示圖片是否下載成功;第二個元素是一個字典。如:

 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複製程式碼

4.爬取結果

執行結果如下:

Scrapy框架之利用ImagesPipeline下載圖片
這裡寫圖片描述

下載成功以後,你就會在剛才設定的儲存圖片的路徑裡看到下載完成的圖片:IMAGES_STORE = 'D:\doubanimgs'

Scrapy框架之利用ImagesPipeline下載圖片
這裡寫圖片描述

5.擴充套件

預設情況下,使用ImagePipeline元件下載圖片的時候,圖片名稱是以圖片URL的SHA1值進行儲存的。

如:
圖片URL:www.example.com/image.jpg
SHA1結果:3afec3b4765f8f0a07b78f98c07b83f013567a0a
則圖片名稱:3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg

如果想進行更改,請參考:使用scrapy框架的ImagesPipeline下載圖片如何保持原檔名呢?


相關文章