Python爬蟲5.9 — scrapy框架下載文件和圖片

綜述

本系列文檔用於對Python爬蟲技術的學習進行簡單的教程講解,鞏固自己技術知識的同時,萬一一不小心又正好對你有用那就更好了。
Python 版本是3.7.4

本篇文章主要pipeline模塊下載文件和圖片的使用。

下載文件和圖片

Scrapy爲下載item中包含的文件(比如再爬取到產品時,同時也想保存到對應圖片)提供了一個可重用的item pipeline。這些pipeline有些共同的方法和結構(我們稱之爲media Pipeline),一般來說我們會使用到File PipelineImages Pipeline

使用Scrapy框架內置方法的好處

我們爲什麼要選擇用Scrapy內置的下載文件的防範:

  1. 避免重複下載最近已經下載過的數據。
  2. 可以方便的指定文件存儲的路徑。
  3. 可以將下載的圖片轉存成通用的格式,比如png或jpg
  4. 可以方便的生成縮略圖。
  5. 可以方便的檢測圖片的寬和高,確保他們滿足最小限制。
  6. 異步下載,效率非常高。

下載文件的File Pipeline

當使用File Pipeline下載文件的時候,按照以下步驟來完成:

  1. 定義好一個Item,然後再這個Item中定義兩個屬性,分別爲file_urls以及filesfile_urls是用來存儲需要下載的文件的url鏈接,需要給一個列表。
  2. 當文件下載完成後,會把文件下載相關的信息存儲到itemfiles屬性中。比如下載路徑、下載的url和文件的校驗碼等。
  3. 在配置文件setting.py中配置FILES_STORE,這個配置時用來設置文件下載下來的路徑。
  4. 啓動pipeline:在ITEM_PIPELINES中設置scrapy.pipelines.files.FilesPipeline:1

下載圖片的Images Pipeline

當使用Images Pipeline下載文件的時候,按照以下步驟來完成:

  1. 定義好一個Item,然後再這個Item中定義兩個屬性,分別爲image_urls以及imagesimage_urls是用來存儲需要下載的圖片的url鏈接,需要給一個列表。
  2. 當文件下載完成後,會把圖片下載相關的信息存儲到itemimages屬性中。比如下載路徑、下載的url和圖片的校驗碼等。
  3. 在配置文件setting.py中配置IMAGES_STORE,這個配置時用來設置文件下載下來的路徑。
  4. 啓動pipeline:在ITEM_PIPELINES中設置scrapy.pipelines.images.ImagesPipeline:1

實例說明

我們就以爬取汽車之家中的圖片爲例。

傳統下載方式

傳統的下載方式在這裏就簡單說下步驟:

  1. 使用scrapy命令創建項目;
  2. 使用命令創建爬蟲;
  3. 更改setting.py文件配置信息;
  4. 編寫items.py代碼;
  5. 編寫spider模塊下代碼(爬取網頁中需要下載的圖片鏈接);
  6. 編寫pipelines.py代碼(進行數據處理,圖片的保存等)。

pipelines.py代碼如下:

import os
import urllib.request


class BmwPipeline(object):

    def __init__(self):
        # 創建文件保存文件夾
        self.image_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
        if not os.path.exists(self.image_path):
            os.mkdir(self.image_path)

    def process_item(self, item, spider):
        category = item['category']
        img_urls = item['img_urls']
        # 創建圖片類別文件夾
        category_path = os.path.join(self.image_path, category)
        if not os.path.exists(category_path):
            os.mkdir(category_path)
        
        # 根據圖片url進行下載保存圖片
        for url in img_urls:
            img_name = url.split('_')[-1]
            urllib.request.urlretrieve(url=url, filename=os.path.join(category_path, img_name))
            print(img_name)

        return item

通過上述代碼我們就可以將爬取的圖片url進行處理保存圖片到本地。但是這種處理方式一個很大的缺點就是圖片只能一個一個下載不能進行異步下載。下面我們使用Scrapy框架中自帶的圖片下載進行下載圖片。

使用Scrapy框架自帶ImagesPipeline進行下載圖片

按照上述的步驟進行編寫相應的代碼:

  1. 定義好一個Item,然後再這個Item中定義兩個屬性,分別爲image_urls以及images。編寫items.py代碼如下:
    import scrapy
    
    
    class BmwItem(scrapy.Item):
        category = scrapy.Field()
        image_urls = scrapy.Field()
        images = scrapy.Field()
    
  2. 在配置文件setting.py中配置IMAGES_STORE。增加代碼如下:
    # 圖片下載路徑,供image pipeline使用
    IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
    
  3. 啓動pipeline:在ITEM_PIPELINES中設置scrapy.pipelines.images.ImagesPipeline:1。修改代碼如下:
    ITEM_PIPELINES = {
        # 'bmw.pipelines.BmwPipeline': 300,
        'scrapy.pipelines.images.ImagesPipeline': 1
    }
    
  4. 編寫spider模塊代碼如下:
    import scrapy
    from bmw.items import BmwItem
    
    class Bmw5Spider(scrapy.Spider):
        name = 'bmw5'
        allowed_domains = ['car.autohome.com.cn']
        start_urls = ['https://car.autohome.com.cn/pic/series/65.html']
    
        def parse(self, response):
            uiboxs = response.xpath('//div[@class="uibox"]')[1:]
    
            for uibox in uiboxs:
                category = uibox.xpath('.//div[@class="uibox-title"]/a/text()').get()
                img_urls_tmp = uibox.xpath('.//ul/li/a/img/@src').getall()
                img_urls = list(map(lambda url:response.urljoin(url),img_urls_tmp))
                item = BmwItem(category=category,image_urls=img_urls)
                yield item
    

運行代碼你會發現其能很快的將圖片下載完成。下載完成後你會在你定義的圖片保存文件夾images下多了一個full文件夾,這個裏面就是所下載下來的所有圖片。有的想問如果我還想要根據分類繼續保存圖片需要怎麼辦呢?這就需要我們去重寫ImagesPipeline類中的一些方法了。通過讀ImagesPipeline類的代碼我們可以知道,類中有兩個方法分別是get_media_requestsfile_path

  • get_media_requests : 這個方法是在發送下載請求之前調用,其實這個方法本身就是去發送下載請求的。
  • file_path : 這個方式是在圖片將要被存儲時候調用,來獲取這個圖片的存儲路徑。

優化方案如下:

  1. pipelines.py中重寫聲明定義一個類,繼承ImagesPipeline;
  2. 在此子類中重新這兩個方法代碼如下:
    import os
    import urllib.request
    
    from scrapy.pipelines.images import ImagesPipeline
    from bmw import settings
    
    
    class BmwImagesPipeline(ImagesPipeline):
    
        # 重寫get_media_requests方法
        def get_media_requests(self, item, info):
            # 這個方法是在發送下載請求之前調用
            # 其實這個方法本身就是去發送下載請求的
            request_objs = super(BmwImagesPipeline, self).get_media_requests(item, info)
            # 將item數據加入到請求中
            for requests_obj in request_objs:
                requests_obj.item = item
            return request_objs
    
        # 重寫file_path方法
        def file_path(self, request, response=None, info=None):
            # 這個方式是在圖片將要被存儲時候調用,來獲取這個圖片的存儲路徑
            # 獲取父類返回的保存地址
            path = super(BmwImagesPipeline, self).file_path(request, response, info)
            
            category = request.item.get('category')
            images_store = settings.IMAGES_STORE
            # 創建圖片分類文件夾
            category_path = os.path.join(images_store, category)
            if not os.path.exists(category_path):
                os.mkdir(category_path)
    
            # 重新返回新的圖片保存路徑
            image_name = path.replace('full/', '')
            image_path = os.path.join(category_path, image_name)
            return image_path
    
  3. 重新修改setting.py中的ITEM_PIPELINES配置如下:
    ITEM_PIPELINES = {
        # 'bmw.pipelines.BmwPipeline': 300,
        # 'scrapy.pipelines.images.ImagesPipeline': 1
        'bmw.pipelines.BmwImagesPipeline': 300,
    }
    

之後運行代碼即可。

其他博文鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章