Scrapy學習篇(九)之文件與圖片下載


Media Pipeline

Scrapy為下載item中包含的文件(比如在爬取到產品時,同時也想保存對應的圖片)提供了一個可重用的 item pipelines . 這些pipeline有些共同的方法和結構(稱之為media pipeline)。我們可以使用FilesPipeline和Images Pipeline來保存文件和圖片,他們有以下的一些特點:

  • Files Pipeline

    • 避免重新下載最近已經下載過的數據
    • 指定存儲路徑

    FilesPipeline的典型工作流程如下:

    1. 在一個爬蟲里,你抓取一個項目,把其中圖片的URL放入 file_urls 組內。
    2. 項目從爬蟲內返回,進入項目管道。
    3. 當項目進入 FilesPipeline,file_urls 組內的URLs將被Scrapy的調度器和下載器(這意味着調度器和下載器的中間件可以復用)安排下載,當優先級更高,會在其他頁面被抓取前處理。項目會在這個特定的管道階段保持“locker”的狀態,直到完成文件的下載(或者由於某些原因未完成下載)。
    4. 當文件下載完后,另一個字段(files)將被更新到結構中。這個組將包含一個字典列表,其中包括下載文件的信息,比如下載路徑、源抓取地址(從 file_urls 組獲得)和圖片的校驗碼(checksum)。 files 列表中的文件順序將和源 file_urls 組保持一致。如果某個圖片下載失敗,將會記錄下錯誤信息,圖片也不會出現在 files 組中。
  • Images Pipeline

    • 避免重新下載最近已經下載過的數據
    • 指定存儲路徑
    • 將所有下載的圖片轉換成通用的格式(JPG)和模式(RGB)
    • 縮略圖生成
    • 檢測圖像的寬/高,確保它們滿足最小限制

    和FilesPipeline類似,除了默認的字段名不同,image_urls保存圖片URL地址,images保存下載后的圖片信息。當然,它還提供了一些拓展功能,比如圖片的縮略圖,過濾圖片的尺寸。
    注意:你需要安裝Pillow 庫來實現以上的拓展功能。

啟用Media Pipeline

要想使用media pipeline,你需要在設置添加一些必要的信息。

# 同時啟用圖片和文件管道
ITEM_PIPELINES = {
                  'scrapy.pipelines.images.ImagesPipeline': 1,
                  'scrapy.pipelines.files.FilesPipeline': 2,
                 }
FILES_STORE = 'D:'  # 文件存儲路徑
IMAGES_STORE = 'D' # 圖片存儲路徑

# 避免下載最近90天已經下載過的文件內容
FILES_EXPIRES = 90
# 避免下載最近90天已經下載過的圖像內容
IMAGES_EXPIRES = 30

# 設置圖片縮略圖
IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (250, 250),
}
# 圖片過濾器,最小高度和寬度,低於此尺寸不下載
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

你可能會好奇文件的命名,在當你啟用media pipeline以后,
它的默認命名方式是這樣的,文件以它們URL的 SHA1 hash 作為文件名。
例如,
對下面的圖片URL:http://www.example.com/image.jpg,
其SHA1 hash 值為:3afec3b4765f8f0a07b78f98c07b83f013567a0a
將被下載並存為下面的文件:<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg
其中,<IMAGES_STORE> 是定義在 IMAGES_STORE 設置里的文件夾,我們設置的是D盤,full 是用來區分圖片和縮略圖(如果使用的話)的一個子文件夾,這個文件夾scrapy會自動生成。

擴展Media Pipeline

下面我們以ImagesPipeline為例來自定義ImagesPipeline,需要重寫以下兩個方法:

  • get_media_requests(item, info)
    在工作流程中可以看到,管道會得到圖片的URL並從項目中下載。為了這么做,你需要重寫 get_media_requests() 方法,並對各個圖片URL返回一個Request:

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

    這些請求將被管道處理,當它們完成下載后,結果將以2元素的元組列表形式傳送到 item_completed() 方法: 每個元組包含 (success, file_info_or_error):

    • success 是一個布爾值,當圖片成功下載時為 True ,因為某個原因下載失敗為False

    • file_info_or_error 是一個包含下列關鍵字的字典(如果成功為 True )或者出問題時為 Twisted Failure 。
      url - 文件下載的url。這是從 get_media_requests() 方法返回請求的url。
      path - 圖片存儲的路徑(類似 IMAGES_STORE)
      checksum - 圖片內容的 MD5 hash
      item_completed() 接收的元組列表需要保證與 get_media_requests() 方法返回請求的順序相一致。下面是 results 參數的一個典型值:

      [(True,
        {'checksum': '2b00042f7481c7b056c4b410d28f33cf',
         'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg',
         'url': 'http://www.example.com/files/product1.jpg'}),
       (False,
        Failure(...))]
      

      該方法 必須返回每一個圖片的URL。

  • item_completed(results, items, info)
    當一個單獨項目中的所有圖片請求完成時,例如,item里面一共有10個URL,那么當這10個URL全部下載完成以后,ImagesPipeline.item_completed() 方法將被調用。默認情況下, item_completed() 方法返回item。

使用ImagesPipeline下載圖片

下面我們用上面學習到的知識來下載一些圖片。
我們以http://jandan.net/ooxx為例,把頁面上的圖片下載下來,並產生縮略圖
我們新建一個項目,名為jiandan,各個文件內容如下。

  • 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   #內置的圖片管道

class JiandanPipeline(ImagesPipeline):#繼承ImagesPipeline這個類

    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

運行這個spider,你會發現,圖片已經下載好了,如下圖所示。


圖片內容你可以自己慢慢看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM