zoukankan      html  css  js  c++  java
  • scrapy——7 scrapy-redis分布式爬虫,用药助手实战,Boss直聘实战,阿布云代理设置

    scrapy——7

    1. 什么是scrapy-redis

    2. 怎么安装scrapy-redis

    3. scrapy-redis常用配置文件

    4. scrapy-redis键名介绍

    5. 实战-利用scrapy-redis分布式爬取用药助手网站

    6. 实战-利用scrapy-redis分布式爬取Boss直聘网站

    7. 如何使用代理


     

    什么是scrapy-redis-->简介

    scrapy-redis是scrapy框架基于redis数据库的组件,用于scrapy项目分布式开发和部署

    特征:

    • 分布式爬取

    你可以启动多个spider工程,相互之间共享单个redis的request队列。最适合广泛的多个域名网站的内容爬取。

    • 分布式数据处理

    爬取到的scrapy的item数据可以推入到redis队列中,这意味着你可以根据需求启动尽可能多的处理器程序来共享item队列,进行item数据持久化处理。

    • scrapy即插即用组件

    Schedule调度器 + Duplication复制过滤器,itemPipleline,基本spider

     什么是scrapy_redis?-->框架

    1. 首先Slaver端从Master端(装有redis的系统)拿任务(Request、url)进行数据抓取,Slaver抓取数据的同时,产生新任务的Request便提交给 Master 处理;

    2. Master端只有一个Redis数据库,负责将未处理的Request去重和任务分配,将处理后的Request加入待爬队列,并且存储爬取的数据

    Scrapy_Redis默认使用的就是这种策略,我们实现起来很简单,因为任务调度等工作Scrapy-Redis都已经帮我们做好了,我们只需要继承RedisSpider、指定redis_key就行了

    缺点是,Scrapy_Redis调度的任务是Request对象,里面信息量比较大(不仅包含url,还有callback函数、headers等信息),可能导致的结果就是会降低爬虫速度、而且会占用Redis大量的存储空间,所以如果要保证效率,那么就需要一定硬件水平。

    怎样安装scrapy-redis

    通过pip安装: pip install scrapy-redis

    依赖环境:

    • python
    • redis
    • scrapy

    官方文档:https://scrapy-redis.readthedocs.io/en/stable/
    源码位置:https://github.com/rmax/scrapy-redis
    博客:https://www.cnblogs.com/kylinlin/p/5198233.html

    怎样使用scrapy-redis?-->常用配置文件

    • (必须)使用了scrapy_redis的去重组件,在redis数据库里做去重
      DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    • (必须)使用了scrapy_redis的调度器,在redis里分配请求
      SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    •  (可选)在redis中保持scrapy-redis用到的各个队列,从而True允许暂停和暂停后和恢复,也就是不清理redis queues
      SCHEDULER_PERSIST = True
    •  (必须)通过配置RedisPipline将item写入key为spider.name:items的redis的list中,供后面的分布式处理item;这个已经由scrapy-redis实现,不需要我们写代码,直接使用即可
      ITEM_PIPELINES = {
          'scrapy_redis.pipelines.RedisPipeline': 100
      }
    •  (必须)指定redis数据库的连接参数
      REDIS_HOST = '127.0.0.1'
      REDIS_PORT = 6379

     怎样使用scrapy-redis?-->redis键名介绍

    •  “项目名: start_urls”
      List 类型,用于获取spider启动时爬取的第一个url
    • “项目名:dupefilter”
      set类型,用于爬虫访问的URL去重
      内容是 40个字符的 url 的hash字符串

    •  “项目名:requests”
      zset类型,用于scheduler调度处理 requests
      内容是 request 对象的序列化 字符串

    •  “项目名:items”
      list 类型,保存爬虫获取到的数据item
      内容是 json 字符串

    实战-利用scrapy-redis分布式写爬虫项目 

    实战·1 爬取用药助手的所有药品简单信息,链接    http://drugs.dxy.cn/index.htm

    首先分析网站,我们发现所有的药理分类用的都是网站内的锚点,获取不到url,所有我们选择获取药品类型进行爬取,查看源代码

    不难看出,所有的h3标签就是我们需要的药品分类url,第一步获取所有的药品分类物品我们就完成了;

    随便点一个进去,我们可以看到红线框中的就是我们需要爬取的数据

    以及翻页

    • 主要的流程分析完毕以后,再用scrapy shell进行测试,这里就不讲解测试的部分了,直接新建项目

    • 我们编写scrapy-redis分布式爬取的时候,编写的逻辑可以先是单纯的scrapy框架,等程序逻辑没有问题以后,再用分布式(导入库,更换父类,定义redis_key,在更改setting)
    • drugsdrugsitems.py    items中,定义我们的数据,这里我们定义  药品名,生产公司,成分,适应症四个数据
      import scrapy
      
      
      class DrugsItem(scrapy.Item):
          # define the fields for your item here like:
          # name = scrapy.Field()
      
          drugs_name = scrapy.Field()
      
          company = scrapy.Field()
      
          composition = scrapy.Field()
      
          indications = scrapy.Field()
    •  为了避免程序出错,先设置部分setting数据
      ROBOTSTXT_OBEY = False
      DOWNLOAD_DELAY = 3
    •  drugsdrugsspidersdrugs_spider.py    编写主要逻辑代码
      # -*- coding: utf-8 -*-
      import scrapy
      import re
      
      from ..items import DrugsItem
      
      
      class DrugsSpiderSpider(RedisSpider):
          name = 'drugs_spider'
          start_urls = ['http://drugs.dxy.cn/index.htm']
      
          def parse(self, response):
              # 匹配每一种药品类型
              category_urls = response.xpath('//ul[@class="ullist clearfix"]/li/h3/a/@href').extract()
      
              for category_url in category_urls:
                  yield scrapy.Request(url=category_url, callback=self.detail_parse)
      
          def detail_parse(self, response):
              try:
                  # 翻页
                  next_url = response.xpath('//a[@title="下一页"]/@href').extract_first()
                  yield scrapy.Request(url=response.url+next_url, callback=self.detail_parse)
                  # 药品名
                  drugs_name = re.findall(r'''<b>商品名:</b>(.*?)&nbsp;&nbsp;''', response.text, re.S)
                  # 公司
                  company = response.xpath('//div[@class="fl"]/h3/a/text()').extract()
                  company = [i.strip().replace('											', '') for i in company]
                  # 成分
                  composition = [i.replace('
      
      ', '') for i in (re.findall(r'''<b>成份:</b>(.*?)<br/>''', response.text, re.S))]
                  # 适应症
                  indications = re.findall(r'''<b>适应症:</b>(.*?)s*</p>''', response.text, re.S)
                  indications = [i.replace('
      
      ', '') for i in indications]
      
                  for drugs_name, company, composition, indications in zip(drugs_name, company, composition, indications):
                      items = DrugsItem()
                      items['drugs_name'] = drugs_name
                      items['company'] = company
                      items['composition'] = composition
                      items['indications'] = indications
      
                      yield items
              except:
                  pass
    •  如果代码完成并无逻辑和编写错误的话,现在就开始就之前的代码进行分布式操作
    • drugsdrugssettings.py    设置setting分布式的必须参数
      ITEM_PIPELINES = {
         'drugs.pipelines.DrugsPipeline': 300, # 这是本身就有的
         'scrapy_redis.pipelines.RedisPipeline': 100 # 这是需要添加进去的
      }
      
      
      DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # scrapy_redis的去重组件
      SCHEDULER = "scrapy_redis.scheduler.Scheduler" # scrapy_redis的调度器
      SCHEDULER_PERSIST = True # 不清理redis queues
      # redis数据库的连接参数(根据个人电脑安装情况定义) REDIS_HOST
      = '127.0.0.1' REDIS_PORT = 6379
    •  drugsdrugsspidersdrugs_spider.py   在之前的spider代码中,导入  from scrapy_redis.spiders import RedisSpider 
    • DrugsSpiderSpider类改成继承RedisSpider,注释掉start_urls,定义redis_key = 'drugs_start:urls'
    • redis_key是固定词,后面的是在redis数据库中需要lpush的键值,如图,其他的不用
    • 接下来要在pipeline中保存数据,将数据保存在MongoDB中   drugsdrugspipelines.py
      import pymongo
      
      class DrugsPipeline(object):
      
          def open_spider(self, spider):
              self.conn = pymongo.MongoClient(host='127.0.0.1', port=27017)
              self.db = self.conn['tanzhou_homework']
              self.connection = self.db['drugs_helper']
      
          def process_item(self, item, spider):
              self.connection.insert(dict(item))
              return item
      
          def close_spider(self,spider):
              pass
    •  然后分布式爬虫就已经完成了,使用scrapy crawl drugs_spider 运行,控制台会阻塞,然后到redis中,使用lpush drugs_start:url http://drugs.dxy.cn/index.htm把需要爬取的链接传入进去,之前阻塞的控制台就会继续运行了,爬虫也就开始了。这样的话,就可以控制台多开了如图0,2,3,4在同时爬取数据,1是redis,5是MondoDB


     

     实战2    爬取Boss直聘

    关于武汉,pthon的工作信息   https://www.zhipin.com/c101200100/?query=python&

    •  分析网站,需要爬取的数据有 岗位,工资,信息,公司,职位描述,发布时间,还有一个翻页标签

    •  思路比上一个要简单一些,这里简单讲解一下
    • items设置数据
      import scrapy
      
      
      class BossItem(scrapy.Item):
          # define the fields for your item here like:
          # name = scrapy.Field()
          job_title = scrapy.Field()
      
          wage = scrapy.Field()
      
          infomation = scrapy.Field()
      
          company = scrapy.Field()
      
          public_time = scrapy.Field()
      
          requirements = scrapy.Field()
    •  setting
      # 爬虫协议
      ROBOTSTXT_OBEY = False
      # 下载延迟
      DOWNLOAD_DELAY = 5
      # 请求头
      DEFAULT_REQUEST_HEADERS = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'en',
        'cookie': '_uab_collina=154233017050308004748422; lastCity=101010100; JSESSIONID=""; ...........,
      }
      # 管道
      ITEM_PIPELINES = {
         'boss.pipelines.BossPipeline': 200,
         'scrapy_redis.pipelines.RedisPipeline': 100
      }
      # redis设置
      DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
      SCHEDULER = "scrapy_redis.scheduler.Scheduler"
      SCHEDULER_PERSIST = True
      REDIS_HOST = '127.0.0.1'
      REDIS_PORT = 6379
    •  boss_spider.py
      # -*- coding: utf-8 -*-
      import scrapy
      from scrapy_redis.spiders import RedisSpider
      from ..items import BossItem
      
      class BossSpiderSpider(RedisSpider):
          name = 'boss_spider'
          # start_urls = ['https://www.zhipin.com/c101200100/?query=python&']
          redis_key = 'zp_start:urls'
      
          def parse(self, response):
              # 翻页
              next_url = response.xpath('//a[@class="next"]/@href').extract_first()
              yield scrapy.Request(url='https://www.zhipin.com'+next_url)
      
              job_title = response.xpath('//div[@class="job-title"]/text()').extract()
      wage
      = response.xpath('//span[@class="red"]/text()').extract()
      info
      = response.xpath('//div[@class="info-primary"]/p/text()').extract() infomations = [info[i:i+3] for i in range(0, len(info), 3)] infomation = [''.join(i) for i in infomations]
      companys
      = response.xpath('//div[@class="company-text"]/h3/a/text()').extract() company = [company for company in companys if ' ' not in company]
      public_time
      = response.xpath('//div[@class="info-publis"]/p/text()').extract()
      detail_urls
      = response.xpath('//div[@class="info-primary"]//h3/a/@href').extract() detail_urls = ['https://www.zhipin.com' + detail_url for detail_url in detail_urls] for url, job_title, wage, infomation, company, public_time in zip(detail_urls, job_title, wage, infomation, company, public_time): item = BossItem() item['job_title'] = job_title, item['wage'] = wage, item['infomation'] = infomation, item['company'] = company, item['public_time'] = public_time yield scrapy.Request(url=url, callback=self.detail_parse, meta={'item':item}) def detail_parse(self, response): item = response.meta.get('item') requirements = response.xpath('string(//div[@class="text"])').extract_first() print(requirements.strip()) item['requirements'] = requirements yield item
    •   运行和上面方法一样,就不多做描述,这里还要讲的东西就是,redis运行以后会有这三个数据,详情请参考前面提到的键名介绍

    运行结果展示

     


    如何使用代理

    在前面的Boss直聘网站数据爬取中,有没有注意到setting中设置了延时为5秒,这是因为Boss直聘反爬还是很有严重的,如果你的短时间内访问的次数过多的话,或者爬取速度过快的话,ip会被封掉,这个时候,我们就需要接入代理了,使用代理爬取数据的话,就不用担心这个问题了。

    网上也有有些免费的代理供游客使用,但是一般能用的都不多,而且也是各种不好用,在这里推荐使布云购买代理使用,经济实惠质量高(绝非广告,只是个人喜好),你也可以选择别的代理产品,例如快代理什么的。

    在网站快代理中,有许多免费的ip代理,如图

    如果可以用的话,在scrapy中的middlewares.py中,新添上代码,

    class process_ProxiesMiddlewares(object):
    
        def process_request(self, request, spider):
            # 知道ip和端口的免费代理
            proxies = 'http://000.00.00.00:0000'
            request.meta['proxy'] = proxies
    
        def process_response(self, request, response, spider):
            # 返回数据的处理
            return .....

    再在setting中,添加如下代码就可以使用了

    DOWNLOADER_MIDDLEWARES = {'boss.middlewares.process_ProxiesMiddlewares': 543 # 添加的
    }

     如果我们选择使用付费的代理,就需要使用别人的API接入,比如之前说到的阿布云代理,scrapy的接入方法如下

        #! -*- encoding:utf-8 -*-
    
        import base64
    
        # 代理服务器
        proxyServer = "http://http-dyn.abuyun.com:9020"
    
        # 代理隧道验证信息
        proxyUser = "H01234567890123D"
        proxyPass = "0123456789012345"
    
        # for Python2
        proxyAuth = "Basic " + base64.b64encode(proxyUser + ":" + proxyPass)
    
        # for Python3
        #proxyAuth = "Basic " + base64.urlsafe_b64encode(bytes((proxyUser + ":" + proxyPass), "ascii")).decode("utf8")
    
        class ProxyMiddleware(object):
            def process_request(self, request, spider):
                request.meta["proxy"] = proxyServer
    
                request.headers["Proxy-Authorization"] = proxyAuth

     requests的接入方法

    #! -*- encoding:utf-8 -*-
    
        import requests
    
        # 要访问的目标页面
        targetUrl = "http://test.abuyun.com"
    
        # 代理服务器
        proxyHost = "http-dyn.abuyun.com"
        proxyPort = "9020"
    
        # 代理隧道验证信息
        proxyUser = "H01234567890123D"
        proxyPass = "0123456789012345"
    
        proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {
          "host" : proxyHost,
          "port" : proxyPort,
          "user" : proxyUser,
          "pass" : proxyPass,
        }
    
        proxies = {
            "http"  : proxyMeta,
            "https" : proxyMeta,
        }
    
        resp = requests.get(targetUrl, proxies=proxies)
    
        print resp.status_code
        print resp.text                    

     还有别的方法不在此叙述,详情可以参考链接

    然后这里主要讲解一下scrapy的使用方法

    import base64
    class ProxyMiddleware(object):
        # 代理服务器
        proxyServer = "http://http-dyn.abuyun.com:9020"
    
        # 代理隧道验证信息  这里填入你购买的验证信息
        proxyUser = "H01234567890123D"
        proxyPass = "0123456789012345"
    
        proxyAuth = "Basic " + base64.urlsafe_b64encode(bytes((proxyUser + ":" + proxyPass), "ascii")).decode("utf8")
    
        def process_request(self, request, spider):
    request.meta[
    "proxy"] = self.proxyServer request.headers["Proxy-Authorization"] = self.proxyAuth def process_response(self, request, response, spider): # 返回数据的处理 if response.status == 200: return response # 不成功重新请求 return request

     setting设置对应的类

    DOWNLOADER_MIDDLEWARES = { 
       'boss.middlewares.process_ProxiesMiddlewares': 543 # 添加的
    }
     

    如此,设置了IP代理的爬虫程序就不怕被封IP了

  • 相关阅读:
    yum puppet dashboard
    puppet常用调试命令
    mysql oracle静默 一键安装脚本
    libvirt 基于C API基本使用案例
    mysql二进制
    mysql启动停止,一台服务器跑 多个mysql数据库
    mysql binaryVInstall
    Centos6.x/Oracle11G 自动化静默安装配置脚本
    最受欢迎linux命令
    xpages很不错的demo
  • 原文地址:https://www.cnblogs.com/pywjh/p/9975444.html
Copyright © 2011-2022 走看看