[235]scrapy分布式爬虫scrapy-redis(二)

===============================================================
    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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值