本篇文章我們以360圖片為例,介紹scrapy框架的使用以及圖片數據的下載。
目標網站:http://images.so.com/z?ch=photography
思路:分析目標網站為ajax加載方式,通過構造目標url從而請求數據,將圖片數據存儲在本地,將圖片的屬性存儲在mongodb中。
1、首先定義我們需要抓取的字段
class ImageItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() collection = 'images' #代表mongodb的的集合名稱 #下面四個字段分別是圖片id,鏈接,標題,縮率圖 id = Field() url = Field() title = Field() thumb = Field()
2、構造我們要爬取的url;由於目標網站時ajax加載的,展示的數據在http://images.so.com/zj?ch=photography&sn=30&listtype=new&temp=1中以json的形式存儲,不斷的下拉頁面之后,每次變化的參數只有sn,並且每次以30的增量增加,第一頁sn=30,第二頁為60,則sn和頁碼的關系為sn*30,所以我們可以構造出url
#定義起始需要爬取的url列表,首先從spider中發送給調度引擎 def start_requests(self): data = {'ch':'photography','listtype':'new','temp':1} base_url = 'http://images.so.com/zj?' for page in range(1,self.settings.get('MAX_PAGE') + 1): #MAX_PAGE以參數的形式在settings文件中配置 data['sn'] = page * 30 url = base_url + urlencode(data) yield Request(url,self.parse)
3、編寫解析函數,返回的數據是json格式
#解析函數 def parse(self, response): result = json.loads(response.text) #將JSON文本字符串轉為JSON對象 for image in result.get('list'): item = ImageItem() item['id'] = image.get('imageid') item['url'] = image.get('qhimg_url') item['title'] = image.get('group_title') item['thumb'] = image.get('qhimg_thumb_url') yield item
4、編寫pipeline文件,將圖片屬性數據存入mongo,將圖片存儲到本地
import pymongofrom scrapy import Request from scrapy.exceptions import DropItem from scrapy.pipelines.images import ImagesPipeline #定義了兩個個Pipeline,分別為數據存儲到mongodb,圖片下載到本地 #在settings中定義Pipeline的執行順序 class MongoPipeline(object): def __init__(self,mongo_url,mongo_db): self.mongo_url = mongo_url self.mongo_db = mongo_db @classmethod def from_crawler(cls,crawler): return cls( mongo_url=crawler.settings.get('MONGO_URL'), mongo_db=crawler.settings.get('MONGO_DB') ) def open_spider(self,spider): self.client = pymongo.MongoClient(self.mongo_url) self.db = self.client[self.mongo_db] def process_item(self, item, spider): self.db[item.collection].insert(dict(item)) return item def close(self,spider): self.client.close()
class ImagesPipeline(ImagesPipeline): def file_path(self,request,response=None,info=None): url = request.url file_name = url.split('/')[-1] return file_name def item_completed(self,results,item,info): image_paths=[x['path'] for ok,x in results if ok] if not image_paths: raise DropItem('Image Download Failed') return item def get_media_requests(self,item,info): yield Request(item['url'])
第一個為MongoPipeline,通過類方法from_crawler從settings文件中獲取mongodb的配置信息,將蜘蛛文件中返回的item存入mongo數據庫。
第二個為ImagesPipeline,scrapy提供了專門處理下載的Pipeline,包括文件下載和圖片下載,下載過程支持異步和多線程,效率極高。
首先定義存儲文件的路徑,需要定義一個IMAGES_STORE變量,在settings文件中添加一行
IMAGES_STORE = './images
我們將路徑定義在當前路徑下的images中,即下載的圖片都會存儲在這個文件夾中。
內置的ImagesPipeline會默認讀取Item對象的image_urls字段,並認為該變量是一個列表,會遍歷這個字段,然后取出url進行圖片下載。
但是我們上面定義的模型可以看出,Item對象的圖片連接並不是image_url,也不是列表形式的,而是單個url,所以為了實現下載,需要重寫ImagePipeline,繼承內置的ImagePileline,再次我們重寫了三個方法,分別是
1)get_media_requests():它的第一個參數item就是爬取生成的Item對象,我們將它的url取出來,直接生成Request對象返回,從而加入調度隊列,等待被調度,執行下載。
2)file_path():它的第一個參數request就是當前下載的Request對象,這個方法用來返回保存的文件名稱,直接將圖片鏈接的最后一部分當做文件名稱即可。
3)item_completed():它是當單個Item完成下載時的處理方法,因為並不是每張圖片都會下載成功,所以需要分析下載結果並剔除下載失敗的圖片,該方法的第一個參數results就是該item對象的下載結果,它是一個列表的形式,列表的每一個元素都是一個元組,其中包含了下載成功或者失敗的信息。
5、在settings中激活pipeline,並配置需要的配置項
ITEM_PIPELINES = { 'images360.pipelines.ImagesPipeline': 300, 'images360.pipelines.MongoPipeline': 301, }
6、執行爬蟲,獲取數據即可。
scrapy crawl images
項目完整代碼:https://gitee.com/liangxinbin/Scrpay/tree/master/images360