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)
  • drugs\drugs\items.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

 

  •  drugs\drugs\spiders\drugs_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('\t\t\t\t\t\t\t\t\t\t\t', '') for i in company]
                # 成分
                composition = [i.replace('\n\n', '') 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('\n\n', '') 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
  •  如果代码完成并无逻辑和编写错误的话,现在就开始就之前的代码进行分布式操作
  • drugs\drugs\settings.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
  •  drugs\drugs\spiders\drugs_spider.py   在之前的spider代码中,导入  from scrapy_redis.spiders import RedisSpider 
  • DrugsSpiderSpider类改成继承RedisSpider,注释掉start_urls,定义redis_key = 'drugs_start:urls'
  • redis_key是固定词,后面的是在redis数据库中需要lpush的键值,如图,其他的不用
  • 接下来要在pipeline中保存数据,将数据保存在MongoDB中   drugs\drugs\pipelines.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 '\n' 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了

 

posted @ 2018-11-17 20:21  pywjh  阅读(1080)  评论(0编辑  收藏  举报
回到顶部