CrawlSpider调用Scrapy_splash功能:Crawlspider的源码详解

本文详细介绍了CrawlSpider的源码分析,包括start_requests()、_parse_response()、_requests_to_follow()和_build_requests()函数的工作流程。强调了在CrawlSpider中如何结合scrapy_splash处理动态渲染页面,并提供了实际应用示例,展示了如何在CrawlSpider中重写相关函数以使用SplashRequest进行请求。

前言

Crawlspider是在scrapy普通Spider的基础上加入内部获取指定规则链接的功能,能有效提高我们获取页面中指定链接的效率。(咱也没测过效率,咱也不敢问),常用于 翻页,网页链接循环的采集任务。
scrapy_splash 是在scrapy框架中用来处理js动态渲染页面的第三方库。使用起来还是挺顺手的。

普通scrpy中使用scrpy_splash还是轻轻松松,干干单单的,将每次请求的scrapy.Request修改为SplashRequest,请求页面就会自动加载动态渲染后的页面,返回数据。

Crawlspider中将请求页面的方法进行了封装,我们的目的就是要找到这个封装的方法,修改为SplashRequest就行了。

Crawspider源码分析

Crawlspider是继承与Spider的对象,所以大体请求方法也是相同的,

1.调用start_requests()函数,请求start_urls中的链接。请求的响应传递给parse()函数。

Crawlspider对象中,对parse()方法进行了重写,所以在 爬虫任务spider中禁止定义同名的parse()函数。
在这里插入图片描述
我们的旅程也就从这里开始启程了。

parse()函数返回的是self._parse_response()函数的结果,同时传入 start_requests()的响应结果,以及一个self.parse_start_url,
cb_kwargs为空字典,follow是一个布尔值True。

self.parse_start_url参数的源码如下:
在这里插入图片描述
可以看到self.parse_start_url在默认值情况下返回的是一个空列表。

2.self._parse_response()函数的源码如下:

在这里插入图片描述
首先判断是否存在 callback,callback参数对应的是self.parse_start_url,第一次self.parse_start_url参数未赋值的情况下为空列表。

判断follow参数和self._follow_links是否同时存在。follow参数是个缺省参数,默认为True。

2.1 self._follow_links源码如下:

在这里插入图片描述
整个函数看起来是重写CrawlSpider父类的set_crawler()函数,我们的重点在下面。

代码意思理解为读取settings中的CRAWLSPIDER_FOLLOW_LINKS变量值。默认为True,意思就是如果不修改settings中的指定变量值的话,self._follow_links的值就为True。

self._parse_response()函数的条件成立,遍历self._requests_to_follow(response)函数的结果。

3. self._requests_to_follow(response)函数的源码如下:

在这里插入图片描述
首先判断响应对象response的类型,类型不是HtmlResponse的话直接返回空值。scrapy_splash生成的对象为SplashTextResponse对象,需要我们重写self._requests_to_follow()函数。

我们的重点是找到CrawlSpider代替手工每次发送的请求。可以看到,前面大部分的逻辑都是用来处理数据,生成新的links,
只有最后一步的遍历中,存在self._build_requests()函数貌似是跟request请求有关的。

4.self._build_requests()函数源码如下:

在这里插入图片描述
果然是scrapy的普通Request请求。请求的是我们的link规则中匹配到的url。同时拥有一个callback回调函数。

self._response_downloaded()函数源码如下:
在这里插入图片描述
又回到了self._parse_response(),重新来一遍流程,直到走完Rules元组中的所有rule规则。这就是最后一步了。

CrawlSpider源码的理解由于知识有限,大多数源码都只是靠猜测意思来完成,不影响主流请求的情况下,也没有深入了解。
希望没有误导各位小伙伴。(手动滑稽保命O(∩_∩)O哈哈~)

最后放上在实际应用中,CrawlSpider与scrapy_splash的结合使用实例。

CrawlSpider与scrapy_splash的使用实例

选择测试的链接为榆次区人民政府的新闻页面。在列表页中,加入了js的动态渲染。页面无法通过普通spider方法获取。

spider代码如下:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_splash import SplashRequest
from scrapy_splash import SplashTextResponse, SplashJsonResponse, SplashResponse
from scrapy.http import HtmlResponse


class BaiduSpider(CrawlSpider):
    name = 'baidu'  # 忽略这个任务名称
    # allowed_domains = ['baidu.com']
    start_urls = ['http://www.yuci.gov.cn/index.html']

    rules = (
        Rule(LinkExtractor(
            restrict_xpaths="//div[@class='bd']/div[@class='conWrap']/div/ul/li[last()]/a[contains(text(),'查看更多')]"),
            callback='parse_item',follow=True),
        Rule(LinkExtractor(restrict_xpaths="//ul[@class='wz_list']/li//a"), callback='parses_item'),
    )

    def start_requests(self):
        for url in self.start_urls:
            print("进入首页")
            print(url)
            yield scrapy.Request(url)

    def _build_request(self, rule, link):
        print("进入下")
        # print(link.url)
        # print(link.text)
        r = SplashRequest(url=link.url, callback=self._response_downloaded, args={"wait": 0.5})
        r.meta.update(rule=rule, link_text=link.text)
        return r

    def _requests_to_follow(self, response):

        # if not isinstance(response, HtmlResponse):
        #     return
        seen = set()
        for n, rule in enumerate(self._rules):
            links = [lnk for lnk in rule.link_extractor.extract_links(response)
                     if lnk not in seen]
            if links and rule.process_links:
                links = rule.process_links(links)
            # print("奈斯")
            print(len(links))
            for link in links:
                # print(link)
                seen.add(link)
                # print("啊哈")
                r = self._build_request(n, link)
                yield rule.process_request(r)

    def parse_item(self, response):
        i = {}
        print("进入列表页")
        print(response)
        return i

    def parses_item(self, response):
        # print("进入详情页")
        name = response.xpath("//h1/text()").extract()
        print(name)
        return ""

首页页面是正常页面,我们在start_requests()函数中使用普通的scrapy.Reuqest()请求。
列表页中页面为JS页面,重写self._build_request()函数,修改请求方法为SplashRequest()。

最终请求结果如下:
在这里插入图片描述

如果我们不重新self._build_request()函数,使用CrawlSpider默认的方法,则发送的为普通Request()请求,无法获取结果。

注意: 重写self._requests_to_follow()函数,目的是为了解除对响应对象的限制,有两种方法:

1.如上面代码所展示的,只需要直接注释掉限制响应对象类型的代码即可。

2.添加允许通过的响应对象类型,比如我们使用的是splash方法,响应对象为SplashTextResponse,
重写self._requests_to_follow()函数,将响应对象类型限制修改为:

   if not isinstance(response, (SplashTextResponse, SplashJsonResponse, SplashResponse, HtmlResponse)):
            return

希望对各位小伙伴有一丢丢的帮助。

我是北房有佳人,咳咳。。。。

参考文章:
https://www.jb51.net/article/129351.htm
https://cuiqingcai.com/4725.html#comments

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值