===============================================================
     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=') # 创建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中
        |
         ---------------------------------------------------------------------------------------------------------------------------------------------------------------------

scrapy分布式爬虫scrapy_redis二篇的更多相关文章

  1. scrapy分布式爬虫scrapy_redis一篇

    分布式爬虫原理 首先我们来看一下scrapy的单机架构:     可以看到,scrapy单机模式,通过一个scrapy引擎通过一个调度器,将Requests队列中的request请求发给下载器,进行页 ...

  2. 第三百五十六节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点

    第三百五十六节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点 1.分布式爬虫原理 2.分布式爬虫优点 3.分布式爬虫需要解决的问题

  3. scrapy 分布式爬虫- RedisSpider

    爬去当当书籍信息 多台机器同时爬取,共用一个redis记录 scrapy_redis 带爬取的request对象储存在redis中,每台机器读取request对象并删除记录,经行爬取.实现分布式爬虫 ...

  4. Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?

    使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...

  5. 三十五 Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点

    1.分布式爬虫原理 2.分布式爬虫优点 3.分布式爬虫需要解决的问题

  6. Scrapy分布式爬虫打造搜索引擎- (二)伯乐在线爬取所有文章

    二.伯乐在线爬取所有文章 1. 初始化文件目录 基础环境 python 3.6.5 JetBrains PyCharm 2018.1 mysql+navicat 为了便于日后的部署:我们开发使用了虚拟 ...

  7. Centos7__Scrapy + Scrapy_redis 用Docker 实现分布式爬虫

    原理:其实就是用到redis的优点及特性,好处自己查--- 1,scrapy 分布式爬虫配置: settings.py BOT_NAME = 'first' SPIDER_MODULES = ['fi ...

  8. 【Python3爬虫】爬取美女图新姿势--Redis分布式爬虫初体验

    一.写在前面 之前写的爬虫都是单机爬虫,还没有尝试过分布式爬虫,这次就是一个分布式爬虫的初体验.所谓分布式爬虫,就是要用多台电脑同时爬取数据,相比于单机爬虫,分布式爬虫的爬取速度更快,也能更好地应对I ...

  9. 【Python3爬虫】学习分布式爬虫第一步--Redis分布式爬虫初体验

    一.写在前面 之前写的爬虫都是单机爬虫,还没有尝试过分布式爬虫,这次就是一个分布式爬虫的初体验.所谓分布式爬虫,就是要用多台电脑同时爬取数据,相比于单机爬虫,分布式爬虫的爬取速度更快,也能更好地应对I ...

随机推荐

  1. OpenCV x64 vs2010 下打开摄像头录制视频写成avi(代码为转载)

    首先参照下面这里进行opencv x64位机器下面的配置 http://wiki.opencv.org.cn/index.php/VC_2010%E4%B8%8B%E5%AE%89%E8%A3%85O ...

  2. The 1st tip of DB Query Analyzer

     The 1st tip of DB Query Analyzer               Ma Genfeng   (Guangdong Unitoll Services incorporate ...

  3. python 字典dict类型合并(不能错过哦)

    我要的字典的键值有些是数据库中表的字段名, 但是有些却不是, 我需要把它们整合到一起, 因此有些这篇文章.(非得凑够150个字,我也是没有办法,扯一点昨天的问题吧,话说python中的session就 ...

  4. IT轮子系列(一)——DropDownList 的绑定,你秒懂了吗

    前言 最近猛然惊觉(说是猛然,是因为自己工作那么多年,居然不自知.不反省),在开发中,自己碰到一些常用的功能代码块,还是习惯性的baidu,然后copy....这样的操作,不知自己重复了多少遍.现在回 ...

  5. List集合中元素排序

    应用场景: 在开发中经常遇到要对List<Object>集合进行排序,并且是根据集合中的对象的某个属性来进行排序    --------以下就此做出的解决方案 public static ...

  6. spring 整合 mybatis 中数据源的几种配置方式

    因为spring 整合mybatis的过程中, 有好几种整合方式,尤其是数据源那块,经常看到不一样的配置方式,总感觉有点乱,所以今天有空总结下. 一.采用org.mybatis.spring.mapp ...

  7. css 字体两端对齐

    我想作为一个前端工作者,总会遇到这样的场景,一个表单展示的字段标题有4个字也有2个字的时候,这样子同时存在想展示的美观一点,就需要字体两端对齐了,其实实现方式很简单,我针对其中一种来做下介绍,以后方法 ...

  8. 与班尼特·胡迪一起攻破浮空城 (HZNU-2264)

    与班尼特·胡迪一起攻破浮空城 AC Time Limit:  1 s      Memory Limit:   256 MB Description 桐人为了拯救被困在浮空城堡最顶层的亚丝娜,决定从第 ...

  9. 导入项目 idea

    下的java核心编程的源码,只有java文件,没有idea或者eclipse的项目结构信息. 分别用eclipse和idea打开了一遍,方便学习调试. 项目文件夹:E:\学习资料\Java\语法\ja ...

  10. php数据导出excel

    /** * 导出数据为excel表格 *@param $data 一个二维数组,结构如同从数据库查出来的数组 *@param $title excel的第一行标题,一个数组,如果为空则没有标题 *@p ...