13-scrapy中selenium的应用

Posted lishuntao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

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

以上是关于13-scrapy中selenium的应用的主要内容,如果未能解决你的问题,请参考以下文章

13.scrapy框架的日志等级和请求传参

Fiddler在selenium调试中的应用

带有Python的Selenium Webdriver - 无法使用Selenium Web驱动程序在Web应用程序中提供输入(Date)

爬虫学习 15.scrapy中selenium的应用

爬虫之scrapy框架应用selenium

selenium模块在爬虫中的应用