scrapy-redis分布式爬虫

一:基本概念

   1:什么是分布式爬虫

     --分布式爬虫,就是用多台电脑,同时运行一个爬虫文件。进行数据的爬去 

  2:原生的scrapy不能实现分布式爬取原因   

    --1:管道不能共享
    --2:调度器不能共享 

 3:使用scrapy-redis组件进行分布式爬虫,scrapy-redis专门为scrapy开发的一套组件。该组件可以使scrapy进行分布式爬虫
   --:准备工作
   --下载scrapy-redis pip install scrapy-redis
   --修改redis.conf配置文件
   --注释掉 bind 127.0.0.1 (不注释,会默认redis只能被本机链接)
   --修改 protected-mode no 关闭保护模式

二:基于RedisCrawlSpider类的分布式爬虫

   1:创建工程

      --scrapy startproject 工程名

      --scrapy genspider -t crawl 爬虫文件名 起始url

   2:导入RedisCrawlSpider

     --修改爬虫文件基于该类的源文件

    

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis import  RedisCrawlSpider
from  redisPRO.items import RedisproItem
#爬去糗事百科糗图的地址
class QiubaiSpider(CrawlSpider):
    name = 'qiubai'
    # allowed_domains = ['www.baidu']
    # start_urls = ['http://www.qiushibaike.com/']

    #调度器名称,和start_urls 作用一样
    redis_key = 'qiubaispider'
    link = LinkExtractor(allow=r'/pic/page\d+')
    rules = (
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )

    def parse_item(self, response):
       div_list=response.xpath('//div[@id="content-left"]')
       for  div in div_list:
           img_url = "https"+div.xpath('.//div[@class="thumb"/a/img/@src]').extract_fist()
           item = RedisproItem()
           item['img_url']=img_url
           yield item

  3:修改setting.py配置文件,将管道和调度器设置成scrapy-redis组件中

      --修改管道

#修改管道
ITEM_PIPELINES = {
   'scrapy_redis.pipelines.RedisPipeline': 400,
}

  --修改调度器

#使用 scrapy_redis去重队列。
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#使用 scrapy_redis调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否保持调度器队列,断点续爬
SCHEDULER_PERSIST = True

  --设置redis ip和端口

#当redis服务器不在本机,需要进行redis配置
REDIS_HOST='192.168.10.11'
REDIS_PORT=6379

  4:进入多台电脑文件目录下,执行爬虫文件

    --scrapy runspider  qiubai.py     #此时所有电脑均处于监听状态

  5:开启redis客户端,将起始的url放入队列中。(爬虫电脑监听成功后,开始运行爬虫文件,并且把结构存储到redis数据库中)

    --lpush  redis-key(对列名称)  起始url  

三:基于RedisSpider类的分布式爬虫

    案例:基于网易新闻文字模块的爬虫

        --ua池               #对请求头进行伪装

        --代理池            #对请求ip进行批量伪装

        --selenium        #scrapy 使用 selenium模块

     1:创建工程 (略)

     2:编写爬虫文件

       a):导入RedisSpider类   from scrapy_redis.spiders import RedisSpider

       b):修改爬虫父类,继承RedisSpider类

class WangyiSpider(RedisSpider):

       c):添加 redis_key属性

redis_key='wangyi'

  d)编写爬虫文件逻辑

# -*- coding: utf-8 -*-
import scrapy

from  wangyiPro.items import WangyiproItem
from selenium import  webdriver
from  scrapy_redis.spiders import RedisSpider
class WangyiSpider(RedisSpider):

    name = 'wangyi'
    # allowed_domains = ['www.wangyi.com']
    # start_urls = ['https://news.163.com/']
    redis_key='wangyi'
    def  __init__(self):
        self.bro = webdriver.Chrome(executable='/Users/yingjianping/Desktop/chromedriver')

    def parse(self, response):
        lis=response.xpath('//div[@class="ns_area list"]/ul/li')
        #只爬取部分模块
        indexs=[3,4,6,7]
        li_list= [lis[index] for index in indexs]
        for  li in li_list:
            url = li.xpath('./a/@href').extract_fist()
            title =li.xpath('./a/text()').extract_fist()
            #对获取的url 进行页面解析,并获取数据信息
            yield  scrapy.Request(url=url,callback=self.secondParse,meta={'titel':title})
    def  get_content(self,response):
        item = response.meta['item']
        content_list = response.xpath('//div[@class="post_text"]/p/text()').extract()
        content = ''.join(content_list)
        item['content']=content
        yield item

    def  secondParse(self,response):
        #动态获取数据的,无法
        div_list=response.xpath('//div[@class="ns_area top_news clearfix"]/')
        for div  in div_list:
             head = div.xpath('div[@class="news_title"]/h3/a/text()').extract_fist()
             url  = div.xpath('div[@class="news_title"]/h3/a/@href').extract_fist()
             img_url =div.xpath('./a/img/src').extract_fist()
             tag ="".join(div.xpath('.//div[@class="new_tag"]//text()'))
             item=WangyiproItem()
             item['img_url']=img_url
             item['url'] = url
             item['tag'] = tag
             item['head'] = head
             item['title']=response.meta['title']
             yield  scrapy.Request(url=url,callback=self.get_content,mate={'item':item})

    def  close(self):
        self.bro.quit()

  e):修改settings文件

         --配置redis,ip,端口,密码(有密码设置密码)

#当redis服务器不在本机,需要进行redis配置
REDIS_HOST='192.168.10.11'
REDIS_PORT=6379
#REDIS_PARAMS={'password':123456}     #有密码配置密码

    --配置管道文件

ITEM_PIPELINES = {
   'scrapy_redis.pipelines.RedisPipeline': 400,
}

  --配置队列

#使用 scrapy_redis去重队列。
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#使用 scrapy_redis调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否保持调度器队列,断点续爬
SCHEDULER_PERSIST = True

  --文件执行 scrapy runspider wangyi.py

      --向redis客户端放入一个起始url

           lpush wangyi www.news.163.com

  3:selenium的使用

         a):使用场景

            --许多网站页面,部分数据是通过动态获取的,在原本的scrapy中获取的请求,不能获取页面内获取动态页面的请求

         b):使用原理

            --在下载器将下载的页面提交给spider文件中,因为获取不到动态页面数据。如果在下载器提交给spider文件过程中,通过selenium修改下载器提交结果,便可以获得完整数据。

         c):使用步骤

             --在爬虫文件中导入webdriver类

from selenium import  webdriver

         --在爬虫实列中,实列化浏览器对象

 def  __init__(self):
        self.bro = webdriver.Chrome(executable='/Users/yingjianping/Desktop/chromedriver')

            --close方法中关闭浏览器驱动器

    def  close(self):
        self.bro.quit()

       --在middlewares.py文件中 导入from scrapy.http import HtmlResponse  用于响应伪装。

           --在middlewares.py文件中重写 RedisproDownloaderMiddleware(object)方法中的proess_response()方法

class RedisproDownloaderMiddleware(object):

    #可以拦截下载器传递给spider的相应对象
    #request:当前相应对象对应的请求对象
    #response:拦截到响应对象
    #spider 当前爬虫文件对应的实例化对象,   比如获取浏览器对象  spider.bro
    #没发一次请求,调用一次该方法
    def process_response(self, request, response, spider):
        #修改响应页面数据
        #1:通过selenium发送请求,去除首页

        if request.url not in ['https://news.163.com/']:
            spider.bro.get(url =request.url)
            #滑动页面使页面加载完全
            js_code ='windon.scrollTo(0,document.body.scrollHeight)'
            spider.pro.execute_script(js_code)
            time.sleep(3)
            page_text=spider.bro.page_sourse
            return HtmlResponse(url=spider.bro.current_url,body=page_text,enconding='utf-8')
        else:
            return response

  --在setting.py文件开启中间件

DOWNLOADER_MIDDLEWARES = {
   'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
    
}

  4:ua池的使用 

      --伪装请求头,防止被识别禁止访问

  --在middleware.py文件中导入 from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware

      --自定义中间件类,继承UserAgentMiddleware类,重写proess_request()方法

import random

user_agents = [
     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
     "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
     "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
     "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
      "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
      "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
      "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
     "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
     "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
     "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
     "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
     "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
     "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
     "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
     "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
 ]
class RandomUserAgent(UserAgentMiddleware):
    def process_request(self, request, spider):
        ua =random.choice(user_agents)
        request.headers.setdefault('User-Agent',ua)

    --setting.py设置开启管道 

DOWNLOADER_MIDDLEWARES = {
   'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
    'wangyiPro.middlewares.RandomUserAgent': 542,
    'wangyiPro.middlewares.RandomProxy': 541,
}

5:代理池(和ua池使用方式类型)

  --自定义中间件类,继承object类,重写proess_request()方法

#注意请求头  我这里写死了
ip_list=[
    'http:39.134.66.72:8080',
    'http:80.68.121.217:34392'
]
class RandomProxy(object):
    def process_request(self, request, spider):
        request.meta['proxy']=random.choice(ip_list)

  --settings.py文件中开启中间件。

 

posted @ 2019-03-20 21:27  pyjar  阅读(202)  评论(0编辑  收藏  举报