===============================================================
Scrapy-Redis分布式爬虫框架
===============================================================
1.Scrapy-Rdis-project: example (Scrapy-Redis分布式爬虫框架----范例说明(Dmoz网站) )
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 1.创建项目--- scrapy startproject example
| example/
| ├── scrapy.cfg
| └── example
| ├── __init__.py
| ├── items.py
| ├── middlewares.py
| ├── pipelines.py
| ├── settings.py
| └── spiders
| ├── __init__.py
| └── my_crawlspider.py/my_redisspider.py/my_rediscrawlspider.py # 3中类型的Scrap-Redis爬虫
|
| 2.明确目标--- vim items.py
| vim items.py
| import scrapy
|
| class ExampleItem(scrapy.Item):
| name = scrapy.Field()
| description = scrapy.Field()
| link = scrapy.Field()
| crawled = scrapy.Field()
| spider = scrapy.Field()
| url = scrapy.Field()
|
| 3.编写自定义pipeline--- vim pipelines.py
| vim pipelins.py
| from datetime import datetime
| class ExampPipeline(object):
| def process_item(self,item,spider):
| item['crawled'] = datetime.utcnow() # 调用datetime.utcnow()方法获取爬虫执行时的UTC时间
| item['spider'] = spider.name # 调用spider.name属性获取当前爬虫名(因为可能同时有多个爬虫在爬取,这样可以看到谁爬了哪些网页)
| return item
|
| 4.注册自定义pipeline及Scrapy-Redis分布式爬虫相关设置--- vim settings.py
| vim settings.py
| #-----------Scrapy-Redis分布式爬虫相关设置如下-------------
| DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件
| SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器
| SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可:
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack"
| SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面)
|
| REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,如果注释或不写会默认将数据存放到本机的Redis中
| REDIS_PORT = 6379 # 注意:master端的Redis需要允许远程连接--配置中注释掉bind 127.0.0.1
|
| #----------注册RedisPipeline/自定义pipeline------------------
| ITEM_PIPELINES = {
| "example.pipelines.ExampPipeline":300, # 自定义pipeline视情况选择性注册(可选)
| "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis)
| } # 注意:自定义pipeline的优先级需高于Redispipeline,因为RedisPipeline不会返回item,所以如果RedisPipeline优先级高于自定义pipeline,那么自定义pipeline无法获取到item
|
| 5.制作爬虫(三种Scrapy-Redis爬虫--- CrawlSpider/RedisSpider/RedisCrawlSpider)
|
|
| 类型一:基于CrawlSpider类的Scrapy-Redis分布式爬虫 (无需任何修改)
| =================================================================================
| (1)生成爬虫--- scrapy genspider -t crawl my_crawlspider "dmoz.org"
| (2)设置爬虫--- vim my_crawlspider.py
| vim my_crawlspider.py
| from scrapy.linkextractor import LinkExtractor
| from scrapy.Spiders import CrawlSpider,Rule
|
| class My_crawlspider(CrawlSpider):
| name = "my_crawlspider"
| allowed_domains = ["dmoz.org"]
| start_urls = ["http://www.dmoz.org/"]
| links = LinkExtractor(restrict_css('.top-cat','.sub-cat','.cat-item'))
| rules = [
| Rule(links,callback='parse_directory',follow=True),
| ]
|
| def parse_directory(self,response):
| for div in response.css('.title-and-desc'):
| yield { 'name':div.css('.site-title::text').extract_first(),
| 'description':div.css('.site-descr::text').extract_first(), # 直接将name/description/link存入Redis数据库
| 'link':div.css('.a::attr(href)').extract(),
| }
| (3)执行爬虫方法--- scrapy crawl my_crawlspider (与正常scrapy一样,无需redis_key,比较鸡肋并不是真正的多机器爬虫)
|
|
|
| 类型二:基于RedisSpider类的Scrapy-Redis分布式爬虫 (需要部分删改)
| =================================================================================
| (1)生成爬虫--- scrapy genspider my_redisspider "dmoz.org"
| (2)设置爬虫--- vim my_redisspider.py
| vim my_redisspider.py
| from scrapy_redis.Spiders import RedisSpider # 变化1:从scrapy_redis.Spiders中引入RedisSpider
|
| class My_redisspiderSpider(RedisSpider): # 变化2:爬虫类所继承的父类变为RedisSpider类
| name = 'my_redisspider'
| redis_key = "my_redisspider:start_urls" # 变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls
|
| def __init__(self,*args,**kwargs): # 变化4:重写__init__方法:动态获取限制域
| domain = kwargs.pop('domain','')
| self.allowed_domain = filter(None,domain,split(','))
| super(My_redisspiderSpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同
|
| def parse(self,response):
| return { 'name':response.css('.site-title::text').extract_first(),
| 'url':respons.url, # 直接将name/url存入Redis数据库
| }
| (3)执行爬虫方法 # 变化5:爬虫执行方法变化
| <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider my_redisspider.py
| <2>在master端redis上发号施令--- lpush my_redisspider:start_urls http://www.dmoz.org/
|
|
|
| 类型三:基于RedisCrawlSpider类飞Scrapy-Redis分布式爬虫 (需要部分删改)
| =================================================================================
| (1)生成爬虫--- scrapy genspider -t crawl my_rediscrawlspider "dmoz.org"
| (2)设置爬虫--- vim my_rediscrawlspider.py
| vim my_rediscrawlspider.py
| from scrapy.linkextractor import LinkExtractor
| from scrapy.Spiders import CrawlSpider,Rule
| from scrapy_redis.Spiders import RedisCrawlSpider # 变化1:从scrapy_redis.Spiders中引入RedisCrawlSpider
|
| class My_rediscrawlspiderSpider(RedisCrawlSpider): # 变化2:爬虫类所继承的父类变为RedisCrawlSpider类
| name = 'my_rediscrawlspider'
| redis_key = 'my_rediscrawlspider:start_urls' # 变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls
|
| rules =[ Rule(LinkExtractor(),callback='parse_page',follow=True), ]
|
| def __init__(self,*args,**kwargs): # 变化4:重写__init__方法:动态获取限制域
| domain = kwargs.pop('domain','')
| self.allowed_domain = filter(None,domain,split(','))
| super(My_rediscrawlspiderSpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同
|
| def parse_page(self,response):
| return { 'name':response.css('.site-title::text').extract_first(),
| 'url':respons.url, # 直接将name/url存入Redis数据库
| }
| (3)执行爬虫方法 # 变化5:爬虫执行方法变化
| <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider my_rediscrawlspider.py
| <2>在master端redis上发号施令--- lpush my_rediscrawlspider:start_urls http://www.dmoz.org/
|
| 小结:
| 如果只想使用Redis的去重和保存功能 ---- 使用类型一
| 如果写分布式 ---- 根据情况选择类型二/类型三
| 如果写聚焦爬虫 ---- 选择类型三
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架进阶1----有缘网:非分布式基于CrawlSpider的scrapy项目 )
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 1.创建项目--- scrapy startproject youyuan
| youyuan/
| ├── scrapy.cfg
| └── youyuan
| ├── __init__.py
| ├── items.py
| ├── middlewares.py
| ├── pipelines.py
| ├── settings.py
| └── spiders
| ├── __init__.py
| └── yy.py
|
| 2.明确目标--- vim items.py
| vim items .py
| improt scrapy
|
| class YouyuanItem(scrapy.Item):
| username = scrapy.Field() # 用户名
| age = scrapy.Field() # 年龄
| header_url = scrapy.Field() # 头像地址
| image_url = scrapy.Field() # 相册个图片地址
| content = scrapy.Field() # 内心独白
| place_from = scrapy.Field() # 籍贯
| education = scrapy.Field() # 教育
| hobby = scrapy.Field() # 爱好
| source_url = scrapy.Field() # 个人主页
| source = scrapy.Field() # 数据来源网站
|
| 3.制作爬虫
| (1)生成爬虫--- scrapy genspider -t crawl yy "youyuan.com"
| (2)设置爬虫--- vim yy.py
| vim yy.py
| import scrapy
| from scrapy.linkextractor import LinkExtractor
| from scrapy.Spiders import CrawlSpider,Rule
| from youyuan.items import YouyuanItem
| import re
|
| class YySpider(CrawlSpider):
| name = "yy"
| allowed_domains = ['youyuan.com']
| start_urls = ["http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/"]
|
| page_links = LinkExtractor(allow=(r'youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/')) # 获取每个显示页的连接
| person_links = LinkExtractor(allow =(r'youyuan.com/\d+-profile/')) # 获取个人主页
| rules = (
| Rule(page_links), # 没有callback/没有follow,默认follow=True继续跟进
| Rule(person_links,callback='parse_item'), # 有callback/没有follow,默认follow=False不继续跟进
| )
| def parse_item(self,response):
| item = YouyuanItem()
| item['username'] = self.get_username()
| item['age'] = self.get_age()
| item['header_url'] = self.get_header_url()
| item['image_url'] = self.get_image_url()
| item['content'] = self.get_content()
| item['place_from'] = self.get_place_from()
| item['education'] = self.get_education()
| item['hobby'] = self.get_hobby()
| item['source_url'] = self.get_source_url()
| item['source'] = self.get_source()
|
| #-----所谓不再使用土枪土炮,看起来高大上的方法----------
| def get_username(self.response):
| username = response.xpath('//dl[@class="person_cen"]//div[@class="main"]/strong/text()').extract()
| if len(username):
| username = username[0]
| else:
| username = "Null"
| return username.strip()
| def get_age(self,response):
| age = response.xpath('//dl/[@class="person_cen"]//dd/p/text()').extract()
| if len(age):
| age = re.find_all(u"\d+岁",age[0])[0]
| else:
| age = "Null"
| return age.strip()
| def get_header_url(self,response):
| header_url = response.xpath('//dl[@class="person_cen"]//dt/img/@src').extract()
| if len (header_url):
| header_url = header_url[0]
| else:
| header_url = "Null"
| return header_url.strip()
| def get_image_url(self,response):
| image_url = response.xpath('//div[@class="ph_show"]/ul/li/a/img/@src').extract()
| if len(image_url):
| image_url = "|".join(image_url) # 这样生成这种形式:xxxx|xxxx|xxxx|
| else:
| image_url = "Null"
| return image_url
| def get_content(self,response):
| content = response.xpath('//div[@class="pre_data"]/ul/li[2]//ol[1]/span/text()').extract()
| if len(content):
| content = content[0]
| else:
| content = "Null"
| return content.strip()
| def get_place_from(self,response):
| place_from = response.xpath('//div[@class="pre_data"]/ul/li[2]//ol[2]/li[1]/span/text()').extract()
| if len(place_from):
| place_from = place_from[0]
| else:
| place_from = "Null"
| return place_from.strip()
| def get_education(self,response):
| education = response.xpath('//div[@class="pre_data"]/ul/li[3]//ol[2]//li[2]/span/text()').extract()
| if len(education):
| education = education[0]
| else:
| education = "Null"
| return education.strip()
| def get_hobby(self,response):
| hobby = response.xpath('//dl[@class="person_cen"]//ol/li/text()').extract()
| if len(hobby):
| hobby = ",".join(hobby).replace(" ","")
| else:
| hobby = "Null"
| return hobby.strip()
|
| 4.编写item pipeline--- vim pipelines.py
| vim pipelines.py
| import json
|
| class YouyuanJsonPipeline(obeject):
| def __init__(self):
| self.f = open("youyuan.json","w")
| def process_item(self,item,spider):
| text = json.dumps(dict(item),ensure_ascii=False) + ",\n"
| self.f.write(item)
| def close_spider(self,spider):
| self.f.close()
|
| 5.启动上述pipeline--- vim settings.py
| vim settings.py
| ITEM_PIPELINES = {"youyuan.pipelines.YouyuanJsonPipeline":300}
|
| 6.执行爬虫--- scrapy crawl yy
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架进阶2----有缘网:非分布式基于CrawlSpider的scrapy项目的数据存入本机Redis )
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 说明:仅仅实在上述scrapy项目的基础上进行settings.py文件的点滴修改,增加一个RedisPipeline而已 <----并不属于Scrapy-Redis分布式
| youyaun/
| ├── scrapy.cfg
| └── youyaun
| ├── __init__.py
| ├── items.py
| ├── middlewares.py
| ├── pipelines.py
| ├── settings.py <----仅对settings.py文件做部分添加修改
| └── spiders
| ├── __init__.py
| └── yy.py
|
| 5.settings.py添加部分信息--- 启用Scrapy-Redis的去重组件/调度器/取出请求方式,以及注册RedisPipeline组件(让数据存入Redis)
| vim settings.py
| DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件
| SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器
| SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可:
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack"
| SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面)
|
| #REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,注释或不写默认将数据存放到本机的Redis中
| #REDIS_PORT = 6379
|
| #----------注册RedisPipeline/自定义pipeline------------------
| ITEM_PIPELINES = {
| "youyuan.pipelines.YouyuanJsonPipeline":300, # 自定义pipeline视情况选择性注册(可选)
| "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis)
| } # 注意:自定义pipeline的优先级需高于Redispipeline,因为RedisPipeline不会返回item,所以如果RedisPipeline优先级高于自定义pipeline,那么自定义pipeline无法获取到item
|
| 6.执行爬虫--- scrapy crawl yy
|
| # 注意: 在原始scrapy项目的基础上,在settings.py文件中添加上述几行设置,就可以将scrapy爬取的数据存放到本机redis中
| # 注意: 上述要想成功保存到本机Redis,有两个前提:本机必须(pip install scrapy-redis) 和本机redis必须启动(redis-server /etc/redis.conf)
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
4.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架进阶3----有缘网:非分布式基于CrawlSpider的scrapy项目 ----> 改写为:RedisCrawlSpider类的Scrapy-Redis分布式爬虫项目 )
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 说明:仅仅实在原始scrapy项目的基础上对settings.py/yy.py文件进行的点滴修改即可
| youyaun/
| ├── scrapy.cfg
| └── youyaun
| ├── __init__.py
| ├── items.py
| ├── middlewares.py
| ├── pipelines.py
| ├── settings.py <----对settings.py文件做部分添加修改(使用Scrapy-Redis的去重组件/调度器/取出请求策略/允许暂停/指明远程Redis主机,并注册RedisPipeline组件)
| └── spiders
| ├── __init__.py
| └── yy.py <----对爬虫文件进行部分修改(引入RedisCrawlSpider爬虫类/去掉allowed_domain/去掉start_urls/增加redis_key/改写init方法动态获取限制域)
|
| 1.修改设置文件--- vim settings.py
| vim settings.py
| DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件
| SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器
| SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可:
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack"
| SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面)
|
| REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,如果注释或不写会默认将数据存放到本机的Redis中
| REDIS_PORT = 6379 # 注意:master端的Redis需要允许远程连接--配置中注释掉bind 127.0.0.1
|
| #----------注册RedisPipeline/自定义pipeline------------------
| ITEM_PIPELINES = {
| "youyuan.pipelines.YouyuanJsonPipeline":300, # 自定义pipeline视情况选择性注册(可选)
| "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis)
| } # 注意:自定义pipeline的优先级需高于Redispipeline,因为RedisPipeline不会返回item,所以如果RedisPipeline优先级高于自定义pipeline,那么自定义pipeline无法获取到item
|
| 2.修改爬虫文件--- vim yy.py
| vim yy.py
| import scrapy
| from scrapy linkextractor import LinkExtractor
| from scrapy.Spiders import CrawlSpider,Rule
| from youyuan.items import YouyuanItem
| import re
| from scrapy_redis.Spiders import RedisCrawlSpider # 变化1:从scrapy_redis.Spiders中引入RedisCrawlSpider
|
| class YySpider(RedisCrawlSpider): # 变化2:爬虫类所继承的父类变为RedisCrawlSpider类
| name = "yy"
| redis_key = "yyspider:start_urls" # 变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls
|
| def __init__(self,*args,**kwargs): # 变化4:重写__init__方法:动态获取限制域
| domain = kwargs.pop('domain','')
| self.allowed_domain = filter(None,domain,split(','))
| super(YySpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同
|
| page_links = LinkExtractor(allow=(r'youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/'))
| person_links = LinkExtractor(allow =(r'youyuan.com/\d+-profile/'))
| .......
| ....... # 后面的代码都相同
|
| 3.爬虫的执行方式改变
| <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider yy.py
| <2>在master端redis上发号施令--- lpush yyspider:start_urls http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
5.Scrapy-Rdis-project: sina2 (Scrapy-Redis分布式爬虫框架----新浪分类资讯:非分布式基于scrapy.Spider的scrapy项目 ----> 改写为:RedisSpider类的Scrapy-Redis分布式爬虫项目 )
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 改写该项目注意:由于改写后数据存放到Redis,所以需要去掉"目录存储路径"相关的代码
|
| 1.创建项目--- scrapy startproject sina2
| sina2/
| ├── scrapy.cfg
| └── sina2
| ├── __init__.py
| ├── items.py
| ├── middlewares.py
| ├── pipelines.py
| ├── settings.py <----对settings.py文件做部分添加修改(使用Scrapy-Redis的去重组件/调度器/取出请求策略/允许暂停/指明远程Redis主机,并注册RedisPipeline组件)
| └── spiders
| ├── __init__.py
| └── xinlang.py <----对爬虫文件进行部分修改(引入RedisCrawlSpider爬虫类/去掉allowed_domain/去掉start_urls/增加redis_key/改写init方法动态获取限制域)
|
| 2.明确目标--- vim items.py
| vim items.py
| import scrapy
|
| class SinaItem(scrapy.Item):
| parent_title = scrap.Field() # 大类标题
| parent_url = scrap.Field() # 大类URL
| sub_title = scrap.Field() # 小类标题
| sub_url = scrap.Field() # 小类URL
| #sub_filename = scrapy.Field() # 小类目录存储路径(数据存放到Redis,不在需要这块代码)
| son_url = scrap.Field() # 小类的子链接
| article_title = scrap.Field() # 文章标题
| article_content = scrap.Field() # 文章内容
|
| 3.制作爬虫
| (1)生成爬虫--- scrapy genspider xinlang "sina.com.cn"
| (2)设置爬虫--- vim xinlang.py
| vim xinlang.py
| import scrapy
| import os
| from sina.items import SinaItem
| import sys
| from scrapy_redis.Spiders import RedisSpider ###<-----变化1:从scrapy_redis.Spiders中引入RedisSpider
|
| reload(sys)
| sys.setdefaultencoding('utf-8')
|
| class XinlangSpider(RedisSpider): ###<-----变化2:爬虫类所继承的父类变为RedisSpider类
| name = 'xinlang'
| redis_key = "xinlangspider:start_urls" ###<-----变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls
|
| def __init__(self,*args,**kwargs): ###<-----变化4:重写__init__方法:动态获取限制域
| domain = kwargs.pop('domain','')
| self.allowed_domain = filter(None,domain,split(','))
| super(XinlangSpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同
|
| def parse(self,response):
| items = []
| parent_title_list = response.xpath('//div[@id=\"tab01\"]/div/h3/a/text()').extract()
| parent_url_list = response.xpath('//div[@id=\"tab01\"]/div/h3/a/@href').extract()
| sub_title_list = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/text()').extract()
| sub_url_list = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/@href').extract()
|
| for i in range(0,len(parent_title_list)):
| #parent_filename = "./Data/" + parent_title_list[i] # 创建大类的存放目录(若存在则不创建,若不存在则重新创建)
| #if(not os.path.exists(parent_filename)): # (数据存放到Redis,不在需要这块代码)
| # os.makedirs(parent_filename)
|
| for j in range(0,len(sub_url_list)): # 实例化SinaItem()并保存大类的URL和标题
| item = SinaItem()
| item['parent_title'] = parent_title_list[i]
| item['parent_url'] = parent_url_list[i]
| if_belong = sub_url_list[i].startwith(item['parent_url']) # 判断小类URL是否以大类URL开头(即判断小类是否属于大类)
| if (if_belong):
| #sub_filename = parent_filename + "/" + sub_title_list[j] # 如果属于该大类,则判断小类存放目录是否存在,不存在则新建该目录
| #if (not os.path.exists(sub_filename)): # (数据存放到Redis,不在需要这块代码)
| # os.makedirs(sub_filename)
| item['sub_title'] = sub_title_list[j] # 保存小类的标题/URL/存放目录,并将目前所获取item信息追加到items列表中保存
| item['sub_url'] = sub_url_list[j]
| item['sub_filename'] = sub_filename
| items.append(item)
| for item in items: # 逐一取出子类的url,并附带上meta信息(即item),将其加入请求队列,使用second_parse()函数处理其返回的响应
| yield scrapy.Request(url=item['sub_url'],meta={'meta_1':item},callback=self.second_parse)
|
| def second_parse(self,response):
| meta_1 = response.meta['meta_1'] # 将meta对应的item信息赋值给meta_1(即,meta_1 = item)
| son_url_list = response.xpath('//a/@href').extract() # 匹配获取返回的孙类的URL列表
| items = []
| for i in range(0,len(son_url_list)): # 循环取出孙类URL判断其是否属于某个大类(以大类的URL开头)和是否是文章(以.shml结尾),如果属于则将该孙类URL保存起来
| if_belong = son_url_list[i].endwith('.shtml') and sub_url_list[i].startwith(meta_1['parent_url'])
| if (if_belong):
| item = SinaItem()
| item['parent_title'] = meta_1['parent_title']
| item['parent_url'] = meta_1['parent_url']
| item['sub_title'] = meta_1['sub_title']
| item['sub_url'] = meta_1['sub_url']
| #item['sub_filename'] = meta_1['sub_filename'] # (数据存放到Redis,不在需要这块代码)
| item['son_url'] = son_url_list[i]
| items.append(item)
| for item in items: # 逐一取出孙类的url,并附带上meta信息(即第二次的item),将其加入请求队列,使用third_parse()函数处理其返回的响应
| yield scrapy.Request(url=item['son_url'],meta={'meta_2':item},callback=self.third_parse)
|
| def third_parse(self,response):
| item = response.meta['meta_2'] # 将meta对应的(第二次获取更新的item信息)赋值给这里的item(即,item = item)
| article_content = "" # 从孙类URL返回响应中匹配出文章标题和文章内容并保存进item
| article_title_list = response.xpath('//hi[@id=\"main_title\"]/text()').extract()
| article_content_list = response.xpath('//div[@id=\"artibody\"]/p/text()').extract()
| for content_part in article_content_list:
| article_content += content_part # 通过循环拼接成完整的文章内容
| item['article_title'] = article_title_list[0]
| item['article_content'] = article_content
| yield item # 将数据收集完整的item传递给pipeline处理
|
| 4.编写item pipelines--- vim pipelines.py (忽略) # (数据存放到Redis,不再需要这块代码,不需要自定义pipeline保存数据到本地)
|
| 5.启用上述pipeline组件--- vim settings.py
| vim settings.py
| DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件
| SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器
| SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可:
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求
| #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack"
| SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面)
|
| REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,如果注释或不写会默认将数据存放到本机的Redis中
| REDIS_PORT = 6379 # 注意:master端的Redis需要允许远程连接--配置中注释掉bind 127.0.0.1
|
| ITEM_PIPELINES = { #"sina.pipelines.SinaSavePipeline":300, # (数据存放到Redis,不再需要这块代码,不需要自定义pipeline保存数据到本地)
| "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis)
| }
|
| 6.爬虫的执行方式改变
| <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider xinlang.py
| <2>在master端redis上发号施令--- lpush yyspider:start_urls http://news.sina.com.cn/guide/
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
6.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架----将Redis中数据持久化存储到MongoDB/MySQL中----> 将有缘网分布式爬取到Redis中的数据转存到MongoDB/MySQL中)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 要将Scrapy-Redis项目爬取到Redis中的数据转存到Mongodb/MySQL中,只需要在项目一级目录下创建两个转存的脚本文件即可
|
| 有缘网Scrapy-Redis项目树形图
| youyuan/
| ├── scrapy.cfg
| ├── process_item_for_mongodb.py <----项目一级目录下创建将Redis数据转存到mongodb的python脚本
| ├── process_item_for_mysql.py <----项目一级目录下创建将Redis数据转存到mysql的python脚本
| └── youyuan
| ├── __init__.py
| ├── items.py
| ├── middlewares.py
| ├── pipelines.py
| ├── settings.py
| └── spiders
| ├── __init__.py
| └── yy.py
|
|
| 1.将Redis数据转存到mongodb中 ----- vim process_item_for_mongodb.py
| vim process_item_for_mongodb.py
| #!/usr/bin/env python
| #coding:utf-8
|
| import redis
| import pymongo
| import json
| def process_item():
| rediscli = redis.Redis(host='200.200.200.200',port=6379,db=0) # 创建Redis连接对象
| mongocli = pymongo.MongoClient(host='200.200.200.202',port=27017) # 创建MongoDB连接对象
| db_name = mongocli['youyuan'] # 利用MongoDB连接对象在MongoDB中创建名为youyuan的数据库对象
| sheet_name = db_name['beijing18-24'] # 利用该数据库对象在youyuan数据库中创建名为beijing18-24的表对象
| count = 0
| while True:
| source,data = rediscli.blpop("yy:items") # 使用循环通过redis连接对象的blpop()方法,不断取出redis中的数据(blpop即FIFO,rlpop即FILO)
| data = json.loads(data) # 将取出的json字符串类型的数据转化为python类型的对象
| sheet_name.insert(data) # 利用mongodb的表对象的insert()方法,向表中插入(刚才转化的python对象)
| count += 1
| print "已经成功从redis转移" + str(count) + "条数据到mongodb"
|
| if __name__ == "__main__":
| process_item()
|
| 注意:MongoDB中可以自动创建键,所以直接执行该脚本就可转移数据
|
|
| 2.将Redis数据转存到mysql中 ----- vim process_item_for_mysql.py
| vim process_item_for_mysql.py
| import redis
| import MySQLdb
| import json
| def process_item():
| rediscli = redis.Redis(host='200.200.200.200',port=6379,db=0) # 创建Redis连接对象
| mysqlcli = MySQLdb.connect(host='200.200.200.204',port 3306,db='youyuan',user='zhangsan',password='123456') # 创建MySQL连接对象
| count = 0
| while True:
| source,data = rediscli.blpop('yy:items') # 使用循环通过redis连接对象的blpop()方法,不断取出redis中的数据(blpop即FIFO,rlpop即FILO)
| data = json.loads(data) # 将取出的json字符串类型的数据转化为python类型的对象
| try:
| cursor = mysqlcli.cursor() # 利用mysql连接对象创建cursor操作游标,并使用该操作游标向Mysql表中插入数据,数据通过python对象获取其值
| cursor.execute("insert into beijing18-24 (username,age,spider,crawled) values(%s,%s,%s,%s)",[data['username'],data['age'],data['spider'],data['crawled']])
| mysqlcli.commit() # 插入完成后需提交事务
| cursor.close() # 关闭操作游标
| count += 1
| print "已经成功从redis转移" + str(count) + "条数据到mongodb"
| except:
| pass
|
| if __name__ == "__main__":
| process_item()
|
| 注意:MySQL中不能自动创建字段,所以在执行该脚本前,需要自行在数据库中创建好响应的数据库/表/字段,然后才能执行该脚本,转移数据到MySQL中
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
来源:https://www.cnblogs.com/psv-fuyang/articles/7891897.html