天天看點

使用Scrapy自帶ImagePipeline下載下傳圖檔使用Scrapy自帶ImagePipeline下載下傳圖檔自定義ImagePipeline

使用Scrapy自帶ImagePipeline下載下傳圖檔

示例代碼

# 啟用scrapy自帶的圖檔下載下傳pipeline
ITEM_PIPELINES = {
  'scrapy.pipelines.images.ImagesPipeline': 1,
}
IMAGES_URLS_FIELD = "front_image_url"    # 設定item中作為圖檔下載下傳連結的item字段
project_dir = os.path.abspath(os.path.dirname(__file__)) 
IMAGES_STORE = os.path.join(project_dir, 'images')   # 設定圖檔存儲路徑
           

注意

  1. 預設的ImagePipeline要求接收的圖檔下載下傳url字段為清單類型,如果不是清單類型将會報錯。
  2. 使用預設ImagePipeline時可能會報這樣的錯誤:ModuleNotFoundError: No module named ‘PIL’,原因是缺少一個pillow的庫,通過pip install pillow指令下載下傳即可

遇到的異常

  1. 圖檔下載下傳字段為空異常且圖檔下載下傳連結不規範
    使用Scrapy自帶ImagePipeline下載下傳圖檔使用Scrapy自帶ImagePipeline下載下傳圖檔自定義ImagePipeline
    使用Scrapy自帶ImagePipeline下載下傳圖檔使用Scrapy自帶ImagePipeline下載下傳圖檔自定義ImagePipeline

解決方案——重載ImagePipeline

有些文章沒有封面圖,也就不會有圖檔下載下傳連結這個值,對應的item中的該字段也就為None,但我設定的沒有比對時預設為‘ ’。但是scrapy自帶的ImagePipeline是預設認位每一個item中對應的圖檔連結字段都有正确可通路的圖檔連結。導緻對于一些沒有該連結的item下載下傳圖檔是報錯異常。解決方法是自定義ImagePipeline,重寫其中的get_media_request()函數,這個函數用來周遊item中的圖檔下載下傳連結字段,生成request,交給scrapy引擎下載下傳圖檔。我在這個方法中添加了判斷是否‘ ’ 的邏輯。

class CustomImagePipeline(ImagesPipeline):  
   
	def get_media_requests(self, item, info):        
		# 重寫ImagesPipeline類的get_media_requests方法        
		# 實作:下載下傳圖檔之前判斷item對應字段的url是否為‘ ’,如果為‘’就跳過下載下傳。       
	    for url in item['cover_image_url']:            
	        if url != '':                
		   try:                    
		       yield Request(url=url)                
		except:                    
		       url = 'https:' + url                    
		       try:                        
			   yield Request(url=url)                    
		       except:                        
			    url = 'http:' + url                       
			    yield Request(url=url)
           

注意

在get_media_request()函數中生成的Request和在spider中使用的Requset是不一樣的,這裡的Request在from scrapy.http import Request這裡,而spider中的Request在from scrapy.spider import Request

自定義ImagePipeline

示例代碼

from scrapy.pipelines.images import ImagesPipeline
from scrapy.http import Request
from scrapy.exceptions import DropItem

 
class CustomImagePipeline(ImagesPipeline):

    def file_path(self, request, response=None, info=None):
        """
        重寫ImagesPipeline類的file_path方法
        實作:下載下傳下來的圖檔命名是以校驗碼來命名的,該方法實作保持原有圖檔命名
        :return: 圖檔路徑
        """
        image_guid = request.url.split('/')[-1]  # 取原url的圖檔命名
        return 'full/%s'% image_guid

    def get_media_requests(self, item, info):
        """
        重寫ImagesPipeline類的get_media_requests方法
        實作:下載下傳圖檔之前判斷item對應字段的url是否為‘’,如果為‘’就跳過下載下傳。
        """
        for url in item['cover_image_url']:
            if url!= '':
                yield Request(url=url)

    def item_completed(self, results, item, info):
        """
        将圖檔的本地路徑指派給item['image_paths']
        :param results:下載下傳結果,二進制組定義如下:(success, image_info_or_failure)。
        第一個元素表示圖檔是否下載下傳成功;第二個元素是一個字典。
        如果success=true,image_info_or_error詞典包含以下鍵值對。失敗則包含一些出錯資訊。
        字典内包含*url:原始URL * path:本地存儲路徑 * checksum:校驗碼
        :param item:
        :param info:
        :return:
        """
        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