scrapy是python裏面一個非常完善的爬蟲框架,實現了非常多的功能,比如內存檢測,對象引用查看,命令行,shell終端,還有各種中間件和擴展等,相信開發過scrapy的朋友都會覺得這個框架非常的強大。但是它有一個致命的缺點,不支持分佈式。所以本文介紹的是scrapy_redis,繼承了scrapy的所有優點,還支持分佈式。
1.安裝scrapy
安裝scrapy非常簡單:
sudo pip install scrapy
sudo pip install scrapy_redis
#如果下載的不順利,可以試試這樣,換一個國內的源,下載速度會飆升
sudo pip install --index https://pypi.mirrors.ustc.edu.cn/simple/ scrapy
在這裏建議開發scrapy_redis使用python 2.7版本,雖然也支持3.x,但總覺得會出bug.
安裝完成,選擇一個恰當的目錄,並進入那個目錄,運行構建項目的命令行即可自動爲我們創建一個spider目錄:
scrapy startproject myspider
簡單的一行即可完成。scrapy有非常多的命令行,大家自行去查詢官方文檔。
2.scrapy_redis原理
scrapy-redis原理:
1.spider解析下載器下載下來的response,返回item或者是links
2.item或者links經過spidermiddleware的process_spider_out()方法,交給engine。
3.engine將item交給itempipeline,將links交給調度器
4.在調度器中,先將request對象利用scrapy內置的指紋函數,生成一個指紋對象
5.如果request對象中的dont_filter參數設置爲False,並且該request對象的指紋不在信息指紋的隊列中,那麼就把該request對象放到優先級的隊列中
6.從優先級隊列中獲取request對象,交給engine
7.engine將request對象交給下載器下載,期間會通過downloadmiddleware的process_request()方法
8.下載器完成下載,獲得response對象,將該對象交給engine,期間會通過downloadmiddleware的process_response()方法
9.engine將獲得的response對象交給spider進行解析,期間會經過spidermiddleware的process_spider_input()方法
10.從第一步開始循環
上面的十個步驟就是scrapy-redis的整體框架,與scrapy相差無幾。本質的區別就是,將scrapy的內置的去重的隊列和待抓取的request隊列換成了redis的集合。就這一個小小的改動,就使得了scrapy-redis支持了分佈式抓取。
在redis的服務器中,會至少存在三個隊列:
a.用於請求對象去重的集合,隊列的名稱爲spider.name:dupefilter,其中spider.name就是我們自定義的spider的名字,下同。
b.待抓取的request對象的有序集合,隊列的名稱爲spider.name:requests
c.保存提取到item的列表,隊列的名稱爲spider.name:items
d.可能存在存放初始url的集合或者是列表,隊列的名稱可能是spider.name:start_urls
如下圖所示
我自定義了一個spider,name屬性爲myspider。當開始運行這個spider的時候,就可以看到在redis的服務器中出現了三個隊列的名字,分別用來去重request對象,存儲提取到的item,存放待抓取的request對象。
那至於spider.name:start_urls這個隊列,裏面存放的是我們第一次啓動爬蟲存放的url,注意是url,而不是scrapy.http.Request對象。如果我們只向這個隊列中存放一條初始的url,那麼這個隊列只會短暫的存在。因爲redis中,如果一個key中沒有數據了,那麼這個key也就消失了。
當然,如果你本身就很瞭解redis的話,這對於你來說,根本就沒有任何難度。
3.編寫scrapy_redis爬蟲
在編寫基於scrapy-redis的爬蟲的時候,我們既可以繼承自scrapy.spiders.Spider這個類,又或者是scrapy.spiders.CrawlSpider,也可以繼承自scrapy-redis的類,比如scrapy_redis.spiders.RedisSpider。
子類化scrapy自身的類時,還是按照scrapy給出的列子一樣,非常的簡單:
from scrapy.spiders import Spider
class MySpider(Spider):
name = 'myspider'
allowed_domains = ['www.example.com']
start_urls = ['http://www.example.com']
def parse(self, response):
#do_something_with_response
這裏有一點需要明確一點,當我們沒有爲request對象顯示的指定一個回調函數時,會使用默認的parse()作爲回調函數。
運行上面的代碼,我們就可以在redis服務器看到前面所說的隊列了。
如果我們是子類化scrapy-redis的spider時,情況有些許的不同:
from scrapy_redis,spiders import RedisSpider
class MySpider(RedisSpider):
name = 'myspider'
redis_key = 'myspider:start_urls'
allowed_domains = ['www.example.com']
def parse(self, response):
#do_something_with_response
這裏我們並沒有指定初始url,所以這就需要我們手動的往redis的初始url隊列中添加url,隊列的名稱爲myspider:start_urls.默認情況下我們採用集合的命令進行添加,要不然會報錯的。
sadd myspider:start_urls http://www.example.com
通過往這個隊列中添加初始url,爬蟲就會開始運行了。直到沒有任何request對象,或者待抓取的url。