scrapy 爬虫之selenium中间件的使用(爬取简书内容)

前几篇文章中介绍了scrapy简单使用,这次我们来看如何在scrapy中使用selenium作为中间件爬取简书的内容。

1、为什么要使用selenium

假设我们要爬取简书的内容,打开简书上的某一篇文章,如:https://www.jianshu.com/p/b35ebb1c1b0e

在chrome 调式控制台中,观察发送的请求,

user_notes 这部分信息是动态渲染的,无法直接获取到。爬取网页内容时,有些网页内容是后端渲染的,在前端展示时,不需要js再次请求获取显示数据,这类网站对于爬取来说易于爬取。现实生活中,这类网站比较少见。目前市面上的大部分网站都是动态的,有的页面做了放爬虫处理,前台界面需要js进行二次请求渲染数据。这类网站,因为数据是动态渲染的,所以直接使用scrapy来爬取,会造成网页内容无法爬取的结果。解决这类问题的思路有两种:

a) 一种是抓包,利用抓包工具,抓包解析二次js数据访问的api接口,然后在scrapy中模拟请求接口,直接获取数据。

b) 另外一种是在python环境中执行js代码,如python工具包,如execjs、PyV8等工具包可以执行js的代码,然后获取数据。

Selenium [1]  是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla FirefoxSafariGoogle ChromeOpera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成 .NetJavaPerl等不同语言的测试脚本。本次,我们直接在scrapy中集成selenium和chrome无头浏览器作为中间件,来帮助我们创建一个完整的浏览器环境。

selenium和chrome driver的安装,详见:Python Selenium库的使用;其中要注意的是,chrome浏览器的版本一定要和chrome driver的版本一致,否则会报错。

2、middleware.py 定义中间件

安装好selenium和chrome driver后,我们在spiders中实现selenium的初始化,具体代码如下:

import scrapy
# from item import CsdnBlogItem
from pro1.items import CsdnBlogItem
import traceback
import logging
import re
from datetime import datetime
from selenium import webdriver


class Spider_CSDN(scrapy.Spider):
    """docstring for SpiderJSscrapy.Spider"
    """
    # spider 的name,每个新建的spider都必须有一个name,此变量是唯一需要定位的实例属性,
    name = "spider_CSDN"
    # 允许爬取的域名,若为空则表示爬取所有网址,无限制
    allowed_domains = ["blog.csdn.net"]
    # 起始url,爬取的起始点,爬虫开始爬取的入口
    start_urls = ["https://blog.csdn.net/csd_ct/article/details/109305242"]

    # 构造函数,初始化chrome webdirver
    def __init__(self, *arg, **args):
        options = webdriver.ChromeOptions()
        # 无头浏览器
        # options.add_argument('--headless')
        # 不使用沙箱
        options.add_argument('--no-sandbox')
        self.browser = webdriver.Chrome(options=options)
        super(Spider_CSDN, self).__init__()

    # 爬虫关闭时,会自动调用closed函数
    def closed(self, reason):
        self.browser.close()

    # 爬取方法
    def parse(self, response):
        '''解析CSDN博客页面中的内容'''
        contentItem = CsdnBlogItem()
        # 请求URL
        contentItem['webUrl'] = response.url.strip().split('?')[0]

        # 文章标题
        .........

middlewares.py的代码如下:


class SeleniumDownloaderMiddleware:
    @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方法,其他的没变
    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        spider.browser.get(request.url)
        return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding='utf8', request=request)

    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

注意在爬虫关闭时,要关闭selenium。有的同学会说,为什么要在spiders中实现spider的初始化,因为在spider中init时,只实例化了一个浏览器对象,如果放在middlewares.py中初始化,会实例化多个浏览器对象,因为没解析一次url,就会调用一次下载中间件。这里可能我们有人可能会纳闷了,为什么通过实现这么一个Downloader Middleware就可以了呢?之前的Request对象怎么办?Scrapy不再处理了吗?Response返回后又传递给了谁来处理?是的,Request对象到这里就不会再处理了,也不会再像以前一样交给Downloader下载了,Response会直接传给Spider进行解析。这究竟是为什么?这时我们需要回顾一下Downloader Middleware的process_request()方法的处理逻辑,在前面我们也提到过,内容如下:当process_request()方法返回Response对象的时候,接下来更低优先级的Downloader Middleware的process_request()和process_exception()方法就不会被继续调用了,转而依次开始执行每个Downloader Middleware的process_response()方法,调用完毕之后直接将Response对象发送给Spider来处理。在这里我们直接返回了一个HtmlResponse对象,它是Response的子类,同样满足此条件,返回之后便会顺次调用每个Downloader Middleware的process_response()方法,而在process_response()中我们没有对其做特殊处理,接着他就会被发送给Spider,传给Request的回调函数进行解析。本段内容参考知乎内容:Scrapy对接Selenium

settings.py文件中,打开下载中间件的设置开关,数字越小级别越高:

3、爬取结果

去掉了--head-less选项,可视化操作,

爬取的内容和以前一样:

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值