zoukankan      html  css  js  c++  java
  • 20200228 scrapy高级使用及分布式

    昨日回顾

    1 安装 pip3 install scrapy
    2 创建项目:scrapy startproject 项目名字
    3 创建爬虫:scrapy genspider 爬虫名字 地址
    4 运行爬虫:scrapy crawl 爬虫名字
    5 目录结构
    6 setting的配置
    7 css选择和xpath选择(extract_first(),extract())
    8 持久化:pipline方式
    

    scrapy高级使用及分布式

    1.爬虫件参数

    yield Request(url=url,callback=self.parse,meta={'item':item})
    
    # 参数
    url			再次请求的地址
    callback	回调函数(处理数据)
    meta		请求传参
    

    2.提升scrapy爬取的效率

    - 在配置文件中进行相关的配置即可:(默认还有一套setting)
        
    #1 增加并发:
    默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。
    
    #2 降低日志级别:
    在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’
    
    # 3 禁止cookie:
    如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False
    
    # 4禁止重试:
    对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False
    
    # 5 减少下载超时:
    如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
    

    3.scrapy的中间件(下载中间件)

    -DownloaderMiddleware
    
    	-process_request
            -retrun None/Respnose/Request/raise
            -None:表示继续处理
            -Respnose:会被引擎调度,进入爬虫
            -Request:会被引擎调度,放到调度器,等待下一次爬取
            -raise:process_exception触发执行
            
    	-process_response
            -Response:继续处理,会被引擎调度,放到爬虫中解析
            -Request:会被引擎调度,放到调度器,等待下一次爬取
            -raise:process_exception触发执行
            
        -process_exception
            -None:表示继续处理
            -Respnose:会被引擎调度,进入爬虫
            -Request:会被引擎调度,放到调度器,等待下一次爬取
    

    process_exception

    def process_exception(self, request, exception, spider):
           from scrapy.http import Request
           print('xxxx')
           # request.url='https://www.baidu.com/'
           request=Request(url='https://www.baidu.com/')
           return request
    

    process_request

    	def process_request(self, request, spider):
            # 请求头
            # print(request.headers)
            # request.headers['User-Agent']=random.choice(self.user_agent_list)
    
            # 设置cookie(并不是所有的请求,都需要带cookie,加一个判断即可)
            # 可以使用cookie池
            # print(request.cookies)
            # # import requests # 如果自己搭建cookie池,这么写
            # # ret=requests.get('127.0.0.1/get').json()['cookie']
            # # request.cookies=ret
            # request.cookies={'name':'lqz','age':18}
    
            # 使用代理(使用代理池)
            # print(request.meta)
            # request.meta['proxy'] = 'http://117.27.152.236:1080'
            return None
    

    process_response

      def process_response(self, request, response, spider):
    				#详见下面
            return response
    

    4.scrapy中使用selenium

    1 在爬虫中启动和关闭selenium
    	-启动
      	bro = webdriver.Chrome('./chromedriver')
      -关闭
      	  def closed(self,spider):
            print("爬虫结束,会走我关了")
            self.bro.close()
         
        
        
        
    2 在下载中间件中写
        def process_response(self, request, response, spider):
            from scrapy.http import Response,HtmlResponse
            # 因为向该地址发请求,不能执行js,现在用selenium执行js,获取执行完的结果,再返回response对象
            url=request.url
            spider.bro.get(url)
            page_source=spider.bro.page_source
            new_response=HtmlResponse(url=url,body=page_source,encoding='utf-8',request=request)
            return new_response
    

    5.去重规则

    如何自己写去重规则,布隆过滤器

    from scrapy.dupefilter import RFPDupeFilter
    from scrapy.core.scheduler import Scheduler
    
    # 整个去重规则是通过RFPDupeFilter中的request_seen控制
    # 在调度器Scheduler中的enqueue_request调用,如果dont_filter=True就不过滤了
    
    # scrapy中如下两个地址,是同一个地址,通过request_fingerprint处理了
    # http://www.baidu.com/?name=lqz&age=18
    # http://www.baidu.com/?age=18&name=lqz
    res1=Request(url='http://www.baidu.com/?name=lqz&age=18')
    res2=Request(url='http://www.baidu.com/?age=18&name=lqz')
    print(request_fingerprint(res1))
    print(request_fingerprint(res2))
    
    
    # 有更省空间的方式
    bitmap方式:比特位:计算机的存储单位  1bit    byte=8个比特位    1kb=1024b   
    布隆过滤器:BloomFilter:原理
    
    
    # 自己重写去重规则
    # 1 在setting中配置
    DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' #默认的去重规则帮我们去重,去重规则在内存中
    # 写一个类,继承BaseDupeFilter,重写方法,主要重写request_seen,如果返回True表示有了,False表示没有
    

    6.分布式爬虫

    # scrapy-redis
    # 概念:整站爬取,假设有9w条连接地址,一台机器一天只能爬3w条,爬3天
    #			现在想用3台机器爬一天
    # scrapy项目部署在3台机器上,三台机器重复的爬9w条,3台机器共享爬取的地址,
    # 3台机器都去一个队列中取地址爬取
    
    #scrapy-redis 重写了Scheduler和pipline
    pip3 install scrapy-redis
    #https://github.com/rmax/scrapy-redis :整个源码总共不超过1000行
    

    使用

    安装scrapy-redis
    pip install scrapy_redis
    

    1 在setting中配置

    # 分布式爬虫的配置
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # Ensure all spiders share same duplicates filter through redis.
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 300
    }
    

    2 修改爬虫类

    	class CnblogsSpider(RedisSpider): # 继承RedisSpider
        name = 'cnblogs'
        allowed_domains = ['www.cnblogs.com']
    
        redis_key = 'myspider:start_urls'  # 原来的start_ulr去掉,携写成这个
    

    3 项目部署

    把项目部署到不同的机器上,或者同一个机器开启多个?
    

    4 redis中写去启动url

    # 启动cmd或者redis服务
    - redis-cli
    - lpush myspider:start_urls https://www.cnblogs.com/(初始网址)
    

    代码

    全站爬取博客园

    run.py启动文件

    #-*- coding: utf-8 -*-
    #!/usr/bin/env python3
    
    ' a test module '
    
    __author__ = 'Fwzzz'
    
    
    from scrapy.cmdline import execute
    
    # execute(['scrapy','crawl','cnblogs','--nolog'])
    # execute(['scrapy','crawl','cnblogs'])
    
    # 分布式爬虫
    execute(['scrapy','crawl','cnblogs_redis','--nolog'])
    
    

    爬虫文件cnblogs.py

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.http import Request
    # 导入items文件类
    from cnblog_pa.items import CnblogPaItem
    
    
    # 了解: 究竟真正的起始爬取的方法在哪  start_requests()
    class CnblogsSpider(scrapy.Spider):
        name = 'cnblogs'
        allowed_domains = ['www.cnblogs.com']
        # start_urls = ['https://www.cnblogs.com//']
    
    
        def start_requests(self):
            # # 深度爬取
            # # 获取所有的页码,进行url拼接  https://www.cnblogs.com/#p2
            # html = Request(url='https://www.cnblogs.com/')
            # last_p = html.css('div[p_200 last]').extract()
            # print(last_p)
            # 需要yield
            yield Request(url='https://www.cnblogs.com/',callback=self.parse)
    
    
    
    
        def parse(self, response):
            # 获取文章块
            div_list = response.css('div.post_item')
            for div in div_list:
                # 声明item
                item = CnblogPaItem()
                # 文章标题
                title = div.xpath('./div[2]/h3/a/text()').extract_first()
                # 文章链接
                url=div.xpath('./div[2]/div/span[2]/a/@href').extract_first()
                # 用户名
                author = div.xpath('./div[2]/div/a/text()').extract_first()
                # 文章简介
                desc = div.xpath('./div[2]/p/text()').extract()[-1].replace('
    ','').replace(' ','')
                # print(title_url,user,desc)
    
                # 持久化的准备传参设置
                item['title'] = title
                item['url'] = url
                item['author'] = author
                item['desc'] = desc
    
    
                # 深度爬取  # 广度爬取
                # yield item对象会去保存 ,request对象会去爬取 # callback回调函数
                yield Request(url=url,callback=self.parse_detail,meta={'item':item})
                # 传参使用meta
    
    
            # 广度爬取下一页
            # 解析下一页的url   css选择器取属性
            next_url = 'https://www.cnblogs.com' + response.css('div.pager>a:last-child::attr(href)').extract_first()
            # print(1111,next_url)
            yield Request(url=next_url,callback=self.parse)
    
    
    
        # 文章详情
        def parse_detail(self,response):
            # 获取传递的item对象
            item = response.meta.get('item')
            # print(item)
            # 深度爬取文章详情
            # 获取文章的html页面
            content = response.css('#post_detail').extract_first()
    
            item['content'] = str(content)
    
            # 存入数据库  (直接跳转到pipeline文件中进行持久化)
            yield item
    

    items.py持久化字段设置

    # -*- coding: utf-8 -*-
    
    # Define here the models for your scraped items
    #
    # See documentation in:
    # https://docs.scrapy.org/en/latest/topics/items.html
    
    import scrapy
    
    # 数据库持久化
    class CnblogPaItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        title = scrapy.Field()
        author = scrapy.Field()
        desc = scrapy.Field()
        content = scrapy.Field()
        url = scrapy.Field()
    
    

    pipelines.py数据库持久化文件

    # -*- coding: utf-8 -*-
    
    # Define here the models for your scraped items
    #
    # See documentation in:
    # https://docs.scrapy.org/en/latest/topics/items.html
    
    import scrapy
    
    # 数据库持久化
    class CnblogPaItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        title = scrapy.Field()
        author = scrapy.Field()
        desc = scrapy.Field()
        content = scrapy.Field()
        url = scrapy.Field()
    
    

    settings文件

    BOT_NAME = 'cnblog_pa'
    
    SPIDER_MODULES = ['cnblog_pa.spiders']
    NEWSPIDER_MODULE = 'cnblog_pa.spiders'
    ROBOTSTXT_OBEY = False
    CONCURRENT_REQUESTS = 32
    
    # 普通使用
    ITEM_PIPELINES = {
       'cnblog_pa.pipelines.CnblogPaPipeline': 300,
    }
    
    
    # 分布式爬虫的设置
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # Ensure all spiders share same duplicates filter through redis.
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 300
    }
    # redis连接的配置  (不配置,默认即可)
    

    补充

    ## 后续
    
    redis高级(持久化rdb和aof,附近的人:地理位置信息,独立用户统计,发布订阅,一主多从,高可用(哨兵)),mongodb,es(集群搭建),docker(镜像,容器,私服搭建,项目部署,dockerfile),mysql主从做读写分离,django中做读写分离,go语言
    
    ## redis
    
    缓存雪崩,缓存穿透
    
    
    
    ## 反扒
    
    0.检测浏览器header
    1 User-Agent,
    2 cookie
    3. ip 封禁
    4. 图片验证码(打码平台,手动)
    5  图片懒加载()
    6 js加密,混淆:pip install PyExecJS 动态执行js
    7 css加密
    8 图片防盗链
    
    
  • 相关阅读:
    MySQL 实训篇
    MySQL 操作部分
    MySQL 数据库设计部分
    Python Excel及setuptool安装
    泛型? extents super
    js中立即执行
    js的闭包
    js作用域与作用域链
    js编译和执行顺序
    文件下载
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12460461.html
Copyright © 2011-2022 走看看