基於 Python 的 Scrapy 爬蟲入門:圖片處理

目錄


上篇文章中講解了如何從網站頁面抓取所須要的數據,很幸運範例中所需的數據是經過 Ajax 請求返回的 JSON 結構化數據,處理起來很簡單,圖片內容也只取了一個連接,對於我最初的目標「把這些圖集添加到本身的博客站點中」這已經夠了,說白了就是「盜鏈」嘛,若是有一天網站作了防盜鏈措施,那這些抄來的圖集就都做廢了,保險的方法就是把圖片也下載到本地,再把圖片連接替換爲本地圖片。html

下載圖片

下載圖片有兩種方式,一種是經過 Requests 模塊發送 get 請求下載,另外一種是使用 Scrapy 的 ImagesPipeline 圖片管道類,這裏主要講後者。數據庫

安裝 Scrapy 時並無安裝圖像處理依賴包 Pillow,需手動安裝不然運行爬蟲出錯。segmentfault

首先在 settings.py 中設置圖片的存儲路徑:數組

IMAGES_STORE = 'D:/'

圖片處理相關的選項還有:scrapy

# 圖片最小高度和寬度設置,能夠過濾過小的圖片
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

# 生成縮略圖選項
IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}

更多選項請參考:https://doc.scrapy.org/en/lat...函數

以前已經存在提取內容的 TuchongPipeline 類,若是使用 ImagePipeline 能夠將提取內容的操做都合併過來,可是爲了更好的說明圖片管道的做用,咱們再單首創建一個 ImagePipeline 類,加到 pipelines.py 文件中,同時重載函數 get_media_requestspost

class PhotoGalleryPipeline(object):
    ...

class PhotoPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        for (id, url) in item['images'].items():
            yield scrapy.Request(url)

上篇文章中咱們把圖片的URL保存在了 item['images'] 中,它是一個字典類型的數組,形如:[{img_id: img_url}, ...],此函數中須要把 img_url 取出並構建爲 scrapy.Request 請求對象並返回,每個請求都將觸發一次下載圖片的操做。網站

settings.py 中註冊 PhotoPipeline,並把優先級設的比提取內容的管道要高一些,保證圖片下載優先於內容處理,目的是若是有圖片下載未成功,經過觸發 DropItem 異常能夠中斷這一個 Item 的處理,防止不完整的數據進入下一管道:編碼

ITEM_PIPELINES = {
    'Toutiao.pipelines.PhotoGalleryPipeline': 300,
    'Toutiao.pipelines.PhotoPipeline': 200,
}

執行爬蟲 scrapy crawl photo ,如無錯誤,在設定的存儲目錄中會出現一個 full 目錄,裏面是下載後的圖片。url

文件名處理

下載的文件名是以圖片URL經過 sha1 編碼獲得的字符,相似 0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg 不是太友好,能夠經過重載 file_path 函數自定義文件名,好比能夠這樣保留原文件名:

...
    def file_path(self, request, response=None, info=None):
        file_name = request.url.split('/')[-1]
        return 'full/%s' % (file_name)
...

上面這樣處理不免會有重名的文件被覆蓋,但參數 request 中沒有過多的信息,不便於對圖片分類,所以能夠改成重載 item_completed 函數,在下載完成後對圖片進行分類操做。

函數 item_completed 的定義:

def item_completed(self, results, item, info)

參數中包含 item ,有咱們抓取的全部信息,參數 results 爲下載圖片的結果數組,包含下載後的路徑以及是否成功下載,內容以下:

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

重載該函數將下載圖片轉移到分類目錄中,同時關聯文件路徑到 item 中,保持內容與圖片爲一個總體:

def item_completed(self, results, item, info):
    image_paths = {x['url'].split('/')[-1]: x['path'] for ok, x in results if ok}
    if not image_paths:
        # 下載失敗忽略該 Item 的後續處理
        raise DropItem("Item contains no files")
    else:
        # 將圖片轉移至以 post_id 爲名的子目錄中
        for (dest, src) in image_paths.items():
            dir = settings.IMAGES_STORE
            newdir = dir + os.path.dirname(src) + '/' + item['post_id'] + '/'
            if not os.path.exists(newdir):
                os.makedirs(newdir)
            os.rename(dir + src, newdir + dest)
    # 將保存路徑保存於 item 中(image_paths 須要在 items.py 中定義)
    item['image_paths'] = image_paths
    return item

接下來在原 TuchongPipeline 類中寫入數據庫的操做中,經過 item['image_paths'] 路徑信息寫入本地圖片連接。

除了 ImagesPipeline 處理圖片外,還有 FilesPipeline 能夠處理文件,使用方法與圖片相似,事實上 ImagesPipelineFilesPipeline 的子類,由於圖片也是文件的一種。

相關文章
相關標籤/搜索