為什么使用分布式爬蟲
分布式:MongoDB的主從(一主多從)
分布式就比如說一個工廠生產(chǎn)線,有車間主人篮灼,車間主人分配任務給一個員工棚辽,和任務分給多個員工的時候兢孝,那是一個人還是多個人的效率高那
同理:
- 單機版爬蟲:
引擎把一個request請求任務放到調度器任務隊列浇坐,向服務器發(fā)起請求的時候會從調度器拿到request請求給下載器發(fā)起請求睬捶。
image.png
-
多臺機器同時爬取數(shù)據(jù)大大提高了爬取的效率。當多臺機器同時進行爬蟲的時候我們需要一個資源管理的容器(調度器)取管理和分配任務
image.png
提取的數(shù)據(jù)近刘、新提取的url會繼續(xù)構建一個request請求交給資源管理器
scrapy-redis
安裝:pip3 install scrapy-redis
- Scrapy_redis在scrapy的基礎上實現(xiàn)了更多擒贸,更強大的功能,具體體現(xiàn)在:reqeust去重觉渴,爬蟲持久化酗宋,和輕松實現(xiàn)分布式
- Scrapy-redis提供了下面四種組件(components):(四種組件意味著這四個模塊都要做相應的修改)
Scheduler
Duplication Filter
Item Pipeline
Base Spider
-
Scrapy_redis是工作流程:
image.png
具體操作:
配置settings文件
# 1:設置去重組件,使用的是scrapy_redis的去重組件疆拘,而不是scrapy自己的去重組件了
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 2:設置調度器,使用scrapy——redis重寫的調度器寂曹,
# 而不再使用scrapy內(nèi)部的調度器了
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 3:可以實現(xiàn)斷點爬取=jondir哎迄,(請求的記錄不會丟失,會存儲在redis數(shù)據(jù)庫中隆圆,
# 不會清楚 redis的隊列漱挚,下次直接從redis的隊列中爬取)
SCHEDULER_PERSIST = True
# 4:設置任務隊列的模式(三選一):
# SpiderPriorityQueue數(shù)據(jù)scrapy-redis默認使用的隊列模式(
# 有自己的優(yōu)先級)默認第一種
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
# 使用了隊列的形式渺氧,任務先進先出旨涝。
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
# 采用了棧的形式:任務先進后出
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
#5: 實現(xiàn)這個管道可以將爬蟲端獲取的item數(shù)據(jù),統(tǒng)一保存在redis數(shù)據(jù)庫中
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
}
# 6:指定要存儲的redis數(shù)據(jù)庫的主機IP
REDIS_HOST = '127.0.0.1' # 遠端的ip地址
# 指定redis數(shù)據(jù)庫主機的端口
REDIS_PORT = 6379
使用scrapy-redis
- 代碼執(zhí)行之后redis自動生成以下內(nèi)容:
"xcfCrawlSpider:requests":存儲的是請求的request對象
"xcfCrawlSpider:items":存儲的爬蟲端獲取的items數(shù)據(jù)
"xcfCrawlSpider:dupefilter":存儲的指紋(為了實現(xiàn)去重)
127.0.0.1:6379> type xcfCrawlSpider:requests
zset
127.0.0.1:6379> type xcfCrawlSpider:items
list
127.0.0.1:6379> type xcfCrawlSpider:dupefilter
set
- 第一種情況:只設置settings文件侣背,并沒有實現(xiàn)分布式白华,只是實現(xiàn)了scrapy_redis的數(shù)據(jù)儲存和去重功能(只實現(xiàn)了存,沒有确纺汀)
import scrapy
from xiachufang.items import XiachufangTagItem,XiachufangCaiPuItem,XiachufangUserInfoItem
class XcfSpider(scrapy.Spider):
name = 'xcf'
allowed_domains = ['xiachufang.com']
#start_urls = ['https://www.xiachufang.com/category/40076/?page=1']
start_urls = ['http://www.xiachufang.com/category/']
def start_requests(self):
pass
- 第二種:通用爬蟲
from scrapy_redis.spiders import RedisCrawlSpider
# 繼承自redis——crawlspider
class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
# 缺少了start_url,多了redis_key:根據(jù)redis_key從redis數(shù)據(jù)庫中獲取任務
redis_key = 'mycrawler:start_urls'
啟動爬蟲
爬蟲出現(xiàn)等待狀態(tài):我們需要在redis中設置起始任務:
redis輸入命令:lpush xcfCrawlSpider:start_urls http://www.xiachufang.com/category/
注意:在redis保存起始url的時候弧腥,windows系統(tǒng)寫url的時候不加引號,ubuntu如果輸入redis命令不生效潮太,url需要加引號
- 第三種情況:實現(xiàn)scrpy.spider爬蟲的分布式爬蟲
from scrapy_redis.spiders import RedisSpider
#繼承自:RedisSpider
class MyCrawler(RedisSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
allowed_domains = ['dmoz.org']
#缺少了start_url,多了redis_key:根據(jù)redis_key從redis
#數(shù)據(jù)庫中獲取任務
redis_key = 'mycrawler:start_urls'
def start_requests(self):
"""
重寫這個方法的目的可以根據(jù)自己的需求發(fā)起請求
:return:
"""
for url in self.start_urls:
yield scrapy.Request(url, callback=self.parse, dont_filter=True)
def parse(self, response):
pass
啟動爬蟲:scrapy crawl 爬蟲名稱
現(xiàn)象:爬蟲處于等待狀態(tài)
需要設置起始任務:
lpush mycrawler:start_urls 目標url
注意:實現(xiàn)scrpy.spider爬蟲的分布式爬蟲第一個回調方法必須是parse管搪,否則代碼無法運行虾攻。第三種情況同樣要注意redis的命令