python爬虫学习笔记-scrapy框架(3)

ImagePipeLines的请求传参

  • 环境安装:pip install Pillow

  • USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36'
    
  • 需求:将图片的名称和详情页中图片的数据进行爬取,持久化存储。

    • 分析:

      • 深度爬取:请求传参
      • 多页的数据爬取:手动请求的发送
    • 爬虫文件:

    • import scrapy
      
      from ..items import DeepimgproItem
      class ImgSpider(scrapy.Spider):
          name = 'img'
          # allowed_domains = ['www.xxx.com']
          start_urls = ['https://pic.netbian.com/4kmeinv/']
          #通用的url模板
          url_model = 'https://pic.netbian.com/4kmeinv/index_%d.html'
          page_num = 2
          def parse(self, response):
              #解析出了图片的名称和详情页的url
              li_list = response.xpath('//*[@id="main"]/div[3]/ul/li')
              for li in li_list:
                  title = li.xpath('./a/b/text()').extract_first() + '.jpg'
                  detail_url = 'https://pic.netbian.com'+li.xpath('./a/@href').extract_first()
                  item = DeepimgproItem()
                  item['title'] = title
                  #需要对详情页的url发起请求,在详情页中获取图片的下载链接
                  yield scrapy.Request(url=detail_url,callback=self.detail_parse,meta={'item':item})
              if self.page_num <= 2:
                  new_url = format(self.url_model%self.page_num)
                  self.page_num += 1
                  yield scrapy.Request(url=new_url,callback=self.parse)
          #解析详情页的数据
          def detail_parse(self,response):
              meta = response.meta
              item = meta['item']
              img_src = 'https://pic.netbian.com'+response.xpath('//*[@id="img"]/img/@src').extract_first()
              item['img_src'] = img_src
      
              yield item
      
    • 管道:

    • # Define your item pipelines here
      #
      # Don't forget to add your pipeline to the ITEM_PIPELINES setting
      # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
      
      
      # useful for handling different item types with a single interface
      import scrapy
      from itemadapter import ItemAdapter
      
      from scrapy.pipelines.images import ImagesPipeline
      class DeepimgproPipeline(ImagesPipeline):
          # def process_item(self, item, spider):
          #     return item
          def get_media_requests(self, item, info):
              img_src = item['img_src']
              #请求传参,将item中的图片名称传递给file_path方法
              #meta会将自身传递给file_path
              print(item['title'],'保存下载成功!')
              yield scrapy.Request(url=img_src,meta={'title':item['title']})
          def file_path(self, request, response=None, info=None, *, item=None):
              #返回图片的名称
              #接收请求传参过来的数据
              title = request.meta['title']
              return title
          def item_completed(self, results, item, info):
              return item
      
      

如何提高scrapy的爬取效率

增加并发:
    默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。

降低日志级别:
    在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为WORNING或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘ERROR’

禁止cookie:
    如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False

禁止重试:
    对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False

减少下载超时:
    如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

post请求发送

  • 问题:在之前代码中,我们从来没有手动的对start_urls列表中存储的起始url进行过请求的发送,但是起始url的确是进行了请求的发送,那这是如何实现的呢?

    • 解答:其实是因为爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就可以对start_urls列表中的url发起请求:

    •   def start_requests(self):
              for u in self.start_urls:
                 yield scrapy.Request(url=u,callback=self.parse)
      
    • 【注意】该方法默认的实现,是对起始的url发起get请求,如果想发起post请求,则需要子类重写该方法。

      • yield scrapy.Request():发起get请求
      • yield scrapy.FormRequest():发起post请求
    • import scrapy
      class FanyiSpider(scrapy.Spider):
          name = 'fanyi'
          # allowed_domains = ['www.xxx.com']
          start_urls = ['https://fanyi.baidu.com/sug']
          #父类中的方法:该方法是用来给起始的url列表中的每一个url发请求
          def start_requests(self):
              data = {
                  'kw':'dog'
              }
              for url in self.start_urls:
                  #formdata是用来指定请求参数
                  yield scrapy.FormRequest(url=url,callback=self.parse,formdata=data)
          def parse(self, response):
              result = response.json()
              print(result)
      

scrapy的核心组件

  • 从中可以大致了解scrapy框架的一个运行机制
- 引擎(Scrapy)
	用来处理整个系统的数据流处理, 触发事务(框架核心)
- 调度器(Scheduler)
	用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
- 下载器(Downloader)
	用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
- 爬虫(Spiders)
	爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
- 项目管道(Pipeline)
	负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

中间件

  • scrapy的中间件有两个:

    • 爬虫中间件
    • 下载中间件
    • 中间件的作用是什么?
      • 观测中间件在五大核心组件的什么位置,根据位置了解中间件的作用
        • 下载中间件位于引擎和下载器之间
        • 引擎会给下载器传递请求对象,下载器会给引擎返回响应对象。
        • 作用:可以拦截到scrapy框架中所有的请求和响应。
          • 拦截请求干什么?
            • 修改请求的ip,修改请求的头信息,设置请求的cookie
          • 拦截响应干什么?
            • 可以修改响应数据
  • 中间件重要方法:

  • # Define here the models for your spider middleware
    #
    # See documentation in:
    # https://docs.scrapy.org/en/latest/topics/spider-middleware.html
    
    from scrapy import signals
    
    # useful for handling different item types with a single interface
    from itemadapter import is_item, ItemAdapter
    
    class MiddleproDownloaderMiddleware:
    
        #类方法:作用是返回一个下载器对象(忽略)
        @classmethod
        def from_crawler(cls, crawler):
            # This method is used by Scrapy to create your spiders.
            s = cls()
            crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
            return s
        #拦截处理所有的请求对象
        #参数:request就是拦截到的请求对象,spider爬虫文件中爬虫类实例化的对象
        #spider参数的作用可以实现爬虫类和中间类的数据交互
        def process_request(self, request, spider):
            return None
        #拦截处理所有的响应对象
        #参数:response就是拦截到的响应对象,request就是被拦截到响应对象对应的唯一的一个请求对象
        def process_response(self, request, response, spider):
            return response
        #拦截和处理发生异常的请求对象
        #参数:reqeust就是拦截到的发生异常的请求对象
        def process_exception(self, request, exception, spider):
            pass
        #控制日志数据的(忽略)
        def spider_opened(self, spider):
            spider.logger.info('Spider opened: %s' % spider.name)
    
    

开发代理中间件

  • request.meta[‘proxy’] = proxy

  • # Define here the models for your spider middleware
    #
    # See documentation in:
    # https://docs.scrapy.org/en/latest/topics/spider-middleware.html
    
    from scrapy import signals
    
    # useful for handling different item types with a single interface
    from itemadapter import is_item, ItemAdapter
    from scrapy import Request
    
    class MiddleproDownloaderMiddleware:
        #类方法:作用是返回一个下载器对象(忽略)
        @classmethod
        def from_crawler(cls, crawler):
            # This method is used by Scrapy to create your spiders.
            s = cls()
            crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
            return s
        #拦截处理所有的请求对象
        #参数:request就是拦截到的请求对象,spider爬虫文件中爬虫类实例化的对象
        #spider参数的作用可以实现爬虫类和中间类的数据交互
        def process_request(self, request, spider):
            #是的所有的请求都是用代理,则代理操作可以写在该方法中
            request.meta['proxy'] = 'http://ip:port'
            #弊端:会使得整体的请求效率变低
            print(request.url+':请求对象拦截成功!')
            return None
        #拦截处理所有的响应对象
        #参数:response就是拦截到的响应对象,request就是被拦截到响应对象对应的唯一的一个请求对象
        def process_response(self, request, response, spider):
            print(request.url+':响应对象拦截成功!')
            return response
        #拦截和处理发生异常的请求对象
        #参数:reqeust就是拦截到的发生异常的请求对象
        #方法存在的意义:将发生异常的请求拦截到,然后对其进行修正
        def process_exception(self, request, exception, spider):
            print(request.url+':发生异常的请求对象被拦截到!')
            #修正操作
            #只有发生了异常的请求才使用代理机制,则可以写在该方法中
            request.meta['proxy'] = 'https://ip:port'
            return request #对请求对象进行重新发送
        #控制日志数据的(忽略)
        def spider_opened(self, spider):
            spider.logger.info('Spider opened: %s' % spider.name)
    
    

开发UA中间件

  • request.headers[‘User-Agent’] = ua

  •     def process_request(self, request, spider):
            request.headers['User-Agent'] = '从列表中随机选择的一个UA值'
            print(request.url+':请求对象拦截成功!')
            return None
    

开发Cookie中间件

  • request.cookies = cookies

  • def process_request(self, request, spider):
        request.headers['cookie'] = 'xxx'
        #request.cookies = 'xxx'
        print(request.url+':请求对象拦截成功!')
        return None
    

selenium+scrapy

  • 需求:将网易新闻中的国内,国际,军事,航空四个板块下的新闻标题和内容进行数据爬取

    • 注意:哪些数据是动态加载的!
    • 技术:selenium,scrapy,中间件
  • 爬虫文件

    • class WangyiSpider(RedisSpider):
          name = 'wangyi'
          #allowed_domains = ['www.xxxx.com']
          start_urls = ['https://news.163.com']
          def __init__(self):
              #实例化一个浏览器对象(实例化一次)
              self.bro = webdriver.Chrome(executable_path='/Users/bobo/Desktop/chromedriver')
      
          #必须在整个爬虫结束后,关闭浏览器
          def closed(self,spider):
              print('爬虫结束')
              self.bro.quit()
      
  • 中间件文件:

    • from scrapy.http import HtmlResponse    
          #参数介绍:
          #拦截到响应对象(下载器传递给Spider的响应对象)
          #request:响应对象对应的请求对象
          #response:拦截到的响应对象
          #spider:爬虫文件中对应的爬虫类的实例
          def process_response(self, request, response, spider):
              #响应对象中存储页面数据的篡改
              if request.url in['http://news.163.com/domestic/','http://news.163.com/world/','http://news.163.com/air/','http://war.163.com/']:
                  spider.bro.get(url=request.url)
                  js = 'window.scrollTo(0,document.body.scrollHeight)'
                  spider.bro.execute_script(js)
                  time.sleep(2)  #一定要给与浏览器一定的缓冲加载数据的时间
                  #页面数据就是包含了动态加载出来的新闻数据对应的页面数据
                  page_text = spider.bro.page_source
                  #篡改响应对象
                  return HtmlResponse(url=spider.bro.current_url,body=page_text,encoding='utf-8',request=request)
              else:
                  return response
      
  • 配置文件:

    • DOWNLOADER_MIDDLEWARES = {
          'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
      
      }
      
  • 最新方式的全栈数据爬取:CrawlSpider的一个爬虫类

  • 分布式

  • 增量式

你可能感兴趣的:(python,开发语言,爬虫,python,学习,scrapy)