上篇文章中講解了如何從網站頁面抓取所須要的數據,很幸運範例中所需的數據是經過 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_requests
:post
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
能夠處理文件,使用方法與圖片相似,事實上ImagesPipeline
是FilesPipeline
的子類,由於圖片也是文件的一種。