scrapy框架之分布式操作
分布式概念
分布式爬虫: 1.概念:多台机器上可以执行同一个爬虫程序,实现网站数据的分布爬取。 2.原生的scrapy是不可以实现分布式爬虫? a)调度器无法共享 b)管道无法共享 3.scrapy-redis组件:专门为scrapy开发的一套组件。该组件可以让scrapy实现分布式。 a)下载:pip install scrapy-redis 4.分布式爬取的流程: a)redis配置文件的配置: i.bind 127.0.0.1 进行注释 ii.protected-mode no 关闭保护模式 b)redis服务器的开启:基于配置配置文件 c)创建scrapy工程后,创建基于crawlSpider的爬虫文件 d)导入RedisCrawlSpider类,然后将爬虫文件修改成基于该类的源文件 e)将start_url修改成redis_key = ‘XXX’ f)在配置文件中进行相应配置:将管道配置成scrapy-redis集成的管道 g)在配置文件中将调度器切换成scrapy-redis集成好的调度器 h)执行爬虫程序:scrapy runspider xxx.py i)redis客户端:lpush 调度器队列的名称 “起始url”
j)keys * redis客户端查看数据,如:lrange qiubai:items 0 -1 【补充】 #如果redis服务器不在自己本机,则需要在setting中进行如下配置 REDIS_HOST = 'redis服务的ip地址' REDIS_PORT = 6379 【注意】近期糗事百科更新了糗图板块的反爬机制,更新后该板块的页码链接/pic/page/2/s=5135066,末尾的数字每次页面刷新都会变化,因此爬虫文件中链接提取器的正则不可写为/pic/page/\d+/s=5135066而应该修改成/pic/page/\d+
一.redis简单回顾
1.启动redis:
mac/linux: redis-server redis.conf
windows: redis-server.exe redis-windows.conf
2.对redis配置文件进行配置:
- 注释该行:bind 127.0.0.1,表示可以让其他ip访问redis
- 将yes该为no:protected-mode no,表示可以让其他ip操作redis
二.scrapy基于redis的数据持久化操作流程
1.安装scrapy-redis组件:
- pip install scrapy-redis
- scrapy-redis是基于scrapy框架开发出的一套组件,其作用就是可以让scrapy实现分布式爬虫。
2.编写爬虫文件:
- 同之前scrapy中基于Spider或者CrawlSpider的编写方式一致。
3.编写管道文件:
- 在scrapy-redis组件中已经帮助我们封装好了一个专门用于连接存储redis数据库的管道(RedisPipeline),因此我们直接使用即可,无需自己编写管道文件。
4.编写配置文件:
- 在settings.py中开启管道,且指定使用scrapy-redis中封装好的管道。
ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 }
- 该管道默认会连接且将数据存储到本机的redis服务中,如果想要连接存储到其他redis服务中需要在settings.py中进行如下配置:
REDIS_HOST = 'redis服务的ip地址' REDIS_PORT = 6379 REDIS_ENCODING = ‘utf-8’ REDIS_PARAMS = {‘password’:’123456’}
三.redis分布式部署
1.scrapy框架是否可以自己实现分布式?
- 不可以。原因有二。
其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的url。(多台机器无法共享同一个调度器)
其二:多台机器爬取到的数据无法通过同一个管道对数据进行统一的数据持久出存储。(多台机器无法共享同一个管道)
2.redis实现分布式基本流程:
- 使用基于scrapy-redis组件中的爬虫文件。
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from movieproject.items import MovieprojectItem #导入scrapy-redis中的模块 from scrapy_redis.spiders import RedisCrawlSpider class NnSpider(RedisCrawlSpider): name = 'nn' allowed_domains = ['www.id97.com'] #redis_key表示调度器中的队列(将要爬取的页面数据对应的url都需要放置到调度器队列中) redis_key = 'nnspider:start_urls' # 根据规则提取所有的页码链接 page_link = LinkExtractor(allow=r'/movie/\?page=\d') detail_link = LinkExtractor(restrict_xpaths='//div[contains(@class,"col-xs-1-5")]/div/a') # detail_link = LinkExtractor(allow=r'/movie/\d+\.html$') # follow : 是否跟进 rules = ( # 所有的页码不用处理,跟进即可 Rule(page_link, follow=True), # 所有的详情页处理,不用跟进 Rule(detail_link, callback='parse_item', follow=False), ) def parse_item(self, response): # 创建一个item对象 item = MovieprojectItem() # 电影海报 item['post'] = response.xpath('//a[@class="movie-post"]/img/@src').extract_first() # 电影名字 item['name'] = response.xpath('//h1').xpath('string(.)').extract_first() yield item
- 使用scrapy-redis组件中封装好的调度器,将所有的url存储到该指定的调度器中,从而实现了多台机器的调度器共享。
# 使用scrapy-redis组件的去重队列 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件自己的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 是否允许暂停 SCHEDULER_PERSIST = True
- 使用scrapy-redis组件中封装好的管道,将每台机器爬取到的数据存储通过该管道存储到redis数据库中,从而实现了多台机器的管道共享。
ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400, }
- 执行:scrapy runspider xxx.py,然后向调度器队列中传入起始url:lpush nnspider:start_urls "http://www.xxx.com/"