• 13-scrapy中selenium的应用


    一. 引入

      在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出来的数据值。但是通过观察我们会发现,通过浏览器进行url请求发送则会加载出对应的动态加载出的数据。那么如果我们想要在scrapy也获取动态加载出的数据,则必须使用selenium创建浏览器对象,然后通过该浏览器对象进行请求发送,获取动态加载的数据值。

    二. 今日详情

    1.案例分析:

    (1).需求: 爬取网易新闻的国内板块下的新闻数据

    (2).需求分析: 当点击国内超链接进入国内对应的页面时, 会发现当前页面展示的新闻数据是被动态加载出来的, 如果直接通过程序对url进行请求, 是获取不到动态加载出的新闻数据的, 则就需要我们使用selenium实例化一个浏览器对象, 在该对象中进行url请求, 获取动态加载的新闻数据

    2. selenium在scrapy中使用的原理分析:

      当引擎将国内板块url对应的请求提交给下载器后,下载器进行网页数据的下载,然后将下载到的页面数据,封装到response中,提交给引擎,引擎将response在转交给Spiders。Spiders接受到的response对象中存储的页面数据里是没有动态加载的新闻数据的。要想获取动态加载的新闻数据,则需要在下载中间件中对下载器提交给引擎的response响应对象进行拦截,切对其内部存储的页面数据进行篡改,修改成携带了动态加载出的新闻数据,然后将被篡改的response对象最终交给Spiders进行解析操作。

    3. selenium在scrapy中的使用流程:

    • 重写爬虫文件的构造方法,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次)
    • 重写爬虫文件的closed(self,spider)方法,在其内部关闭浏览器对象。该方法是在爬虫结束时被调用
    • 重写下载中间件的process_response方法,让该方法对响应对象进行拦截,并篡改response中存储的页面数据
    • 在配置文件中开启下载中间件

    4. 代码展示:

      爬虫文件代码:

    # -*- coding: utf-8 -*-
    import scrapy
    from Wangyixinwen.items import WangyixinwenItem
    from selenium import webdriver
    
    class WangyiSpider(scrapy.Spider):
        name = 'wangyi'
        # allowed_domains = ['www.xxx.com']
        #自动发送请求的 去设置里将user-agent以及robots协议设置好
        start_urls = ['https://news.163.com/']
        """"
        我们的第一次请求对象就是start_urls开始,我们还需要对UA伪装同时还有IP代理等,可以在下载中间件的process_request中设置UA伪装
        process_exception中设置代理ip(等到自己ip出问题了就用代理ip)。等到响应对象缺失就可以在process_response中拦截。
        """
        bro = webdriver.Chrome("./chromedriver.exe")
        urls = []
        def parse(self, response):
            #从响应对象中获取到全部目标
            target_list = response.xpath('//*[@id="js_festival_wrap"]/div[3]/div[2]/div[2]/div[2]/div/ul/li')
            #遍历目标得到具体的url
            for index in [3,6,7,8]:
                target = target_list[index]
                #得到目标的url 并且取出url
                target_url = target.xpath('./a/@href').extract_first()
                self.urls.append(target_url)
                #对目标url发起请求
                yield scrapy.Request(url=target_url,callback=self.parse_target)
        def parse_target(self,response):
            #将第二次请求的响应对象开始解析,分析由于还未编写代码就知道这次是含有动态加载数据,因此这次
            #分析可以用到selenium一起解析数据,下面解析出新闻标题以及具体的详情页的url(响应的数据缺失,因此我们需要去下载中间件设置)
            detail_list = response.xpath('/html/body/div[1]/div[3]/div[4]/div[1]/div/div/ul/li/div/div')
            for detail in detail_list:
                title = detail.xpath('./div/div[1]/h3/a/text()').extract_first()
                detail_url = detail.xpath('./div/div[1]/h3/a/@href').extract_first()
                #实例化item对象,封装数据
                item = WangyixinwenItem()
                item["title"] = title
                yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={"item":item})
        def parse_detail(self,response):
            item = response.meta['item']
            content = response.xpath('//*[@id="endText"]/p/text()').extract()
            #将内容转换为字符串对象
            content = "".join(content)
            item["content"] = content
            #提交数据
            yield item
        def close(self,spider):
            # 爬虫结束,浏览器也关闭
            print("爬虫结束!!!")
            self.bro.quit()

     中间件文件拦截代码展示:

    # -*- coding: utf-8 -*-
    
    # 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
    from scrapy.http import HtmlResponse
    import random
    from time import sleep
    
    class WangyixinwenDownloaderMiddleware(object):
        # Not all methods need to be defined. If a method is not defined,
        # scrapy acts as if the downloader middleware does not modify the
        # passed objects.
       
        def process_request(self, request, spider):
        
            return None
    
        def process_response(self, request, response, spider):
            #刚才响应体缺失,因此从这里我们应该重新返回新的响应体
            #这里要用到爬虫程序中的urls,判断url是否在里面,在urls里面的就会出现响应缺失,
            # 、因此需要返回新的响应体
            if request.url in spider.urls:
                #响应缺失是因为是动态加载数据,因此我们配合selenium使用
                #在这里实例化selenium的话会被实例化多次,然而selenium只需要实例化一次,
                #这个时候我们可以将selenium放在实例化一次的爬虫程序开始的时候,实例化完成引入
                sleep(2)
                bro = spider.bro.get(url=request.url)#浏览器中发送请求
                sleep(1)
                spider.bro.execute_script("window.scrollTo(0,document.body.scrollHeight)")
                sleep(1.5)
                spider.bro.execute_script("window.scrollTo(0,document.body.scrollHeight)")
                sleep(0.7)
                spider.bro.execute_script("window.scrollTo(0,document.body.scrollHeight)")
                sleep(1)
                spider.bro.execute_script("window.scrollTo(0,document.body.scrollHeight)")
                #发送到请求我们需要获取浏览器当前页面的源码数据 获取数据之前需要翻滚页面
                page_text = spider.bro.page_source
                #改动返回响应对象  scrapy提供了一个库url=spider.bro.current_url, body=page_text, encoding='utf-8', request=request
                new_response = HtmlResponse(url=request.url,body=page_text,encoding="utf-8",request=request)
                return new_response
            else:
                return response
            #提交完新的响应体之后,去设置将下载中间件打开
    
        def process_exception(self, request, exception, spider):
            pass

    备注:仔细看代码注释,能够迅速唤醒记忆,更快开发。

  • 相关阅读:
    TCP_UCP通信原理及案例
    JavaScript高级笔记DOM与BOM
    JavaScript基本语法,基本对象,正则表达式
    JDBC连接池&JDBCTemplate&Sping JDBC
    JDBC,JDBCUtils,JDBC控制事务
    自动化工具ansible(0——准备部署工作)
    监控软件篇——prometheus+exporter组件+grafana
    命令工具篇
    sed 命令备忘
    ES6数据分组
  • 原文地址:https://www.cnblogs.com/lishuntao/p/11629852.html
Copyright © 2020-2023  润新知