Scrapy 源码剖析(三)Scrapy 有哪些核心组件?

本文深入剖析Scrapy的四个核心组件:引擎、调度器、下载器和爬虫类。引擎是协调各个组件的中心,调度器负责任务调度,下载器处理网络请求,而爬虫类则定义了抓取规则。调度器通过请求指纹过滤器避免重复请求,下载器中间件和爬虫中间件提供了定制化处理逻辑。
摘要由CSDN通过智能技术生成

微信搜索关注「水滴与银弹」公众号,第一时间获取优质技术干货。7年资深后端研发,用简单的方式把技术讲清楚。

在上一篇文章:Scrapy 源码剖析(二)Scrapy 是如何运行起来的?,我们主要剖析了 Scrapy 是如何运行起来的核心逻辑,也就是在真正执行抓取任务之前,Scrapy 都做了哪些工作。

这篇文章,我们就来进一步剖析一下,Scrapy 有哪些核心组件?以及它们主要负责了哪些工作?这些组件为了完成这些功能,内部又是如何实现的。

爬虫类

我们接着上一篇结束的地方开始讲起。上次讲到 Scrapy 运行起来后,执行到最后到了 Crawlercrawl 方法,我们来看这个方法:

@defer.inlineCallbacks
def crawl(self, *args, **kwargs):
    assert not self.crawling, "Crawling already taking place"
    self.crawling = True
    try:
        # 从spiderloader中找到爬虫类 并实例化爬虫实例
        self.spider = self._create_spider(*args, **kwargs)
        # 创建引擎
        self.engine = self._create_engine()
        # 调用爬虫类的start_requests方法 拿到种子URL列表
        start_requests = iter(self.spider.start_requests())
        # 执行引擎的open_spider 并传入爬虫实例和初始请求
        yield self.engine.open_spider(self.spider, start_requests)
        yield defer.maybeDeferred(self.engine.start)
    except Exception:
        if six.PY2:
            exc_info = sys.exc_info()
        self.crawling = False
        if self.engine is not None:
            yield self.engine.close()
        if six.PY2:
            six.reraise(*exc_info)
        raise

执行到这里,我们看到首先创建了爬虫实例,然后创建了引擎,最后把爬虫交给引擎来处理了。

在上一篇文章我们也讲到,在 Crawler 实例化时,会创建 SpiderLoader,它会根据我们定义的配置文件 settings.py 找到存放爬虫的位置,我们写的爬虫代码都在这里。

然后 SpiderLoader 会扫描这些代码文件,并找到父类是 scrapy.Spider 爬虫类,然后根据爬虫类中的 name 属性(在编写爬虫时,这个属性是必填的),生成一个 {spider_name: spider_cls} 的字典,最后根据 scrapy crawl <spider_name> 命令中的 spider_name 找到我们写的爬虫类,然后实例化它,在这里就是调用了_create_spider方法:

def _create_spider(self, *args, **kwargs):
    # 调用类方法from_crawler实例化
    return self.spidercls.from_crawler(self, *args, **kwargs)

实例化爬虫比较有意思,它不是通过普通的构造方法进行初始化,而是调用了类方法 from_crawler 进行的初始化,找到 scrapy.Spider 类:

@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
    spider = cls(*args, **kwargs)
    spider._set_crawler(crawler)
    return spider
    
def _set_crawler(self, crawler):
    self.crawler = crawler
    # 把settings对象赋给spider实例
    self.settings = crawler.settings
    crawler.signals.connect(self.close, signals.spider_closed)

在这里我们可以看到,这个类方法其实也是调用了构造方法,进行实例化,同时也拿到了 settings 配置,来看构造方法干了些什么?

class Spider(object_ref):
    name = None
    custom_settings = None

    def __init__(self, name=None, **kwargs):
        # name必填
        if name is not None:
            self.name = name
        elif not getattr(self, 'name', None):
            raise ValueError("%s must have a name" % type(self).__name__)
        self.__dict__.update(kwargs)
        # 如果没有设置start_urls 默认是[]
        if not hasattr(self, 'start_urls'):
            self.start_urls = []

看到这里是不是很熟悉?这里就是我们平时编写爬虫类时,最常用的几个属性:namestart_urlscustom_settings

  • name:在运行爬虫时通过它找到我们编写的爬虫类;
  • start_urls:抓取入口,也可以叫做种子URL;
  • custom_settings:爬虫自定义配置,会覆盖配置文件中的配置项;
    在这里插入图片描述

引擎

分析完爬虫类的初始化后,还是回到 Crawlercrawl 方法,紧接着就是创建引擎对象,也就是 _create_engine 方法,看看初始化时都发生了什么?

class ExecutionEngine(object):
    """引擎"""
    def __init__(self, crawler, spider_closed_callback):
        self.crawler = crawler
        # 这里也把settings配置保存到引擎中
        self.settings = crawler.settings
        # 信号
        self.signals = crawler.signals
        # 日志格式
        self.logformatter = crawler.logformatter
        self.slot = None
        self.spider = None
        self.running = False
        self.paused = False
        # 从settings中找到Scheduler调度器,找到Scheduler类
        self.scheduler_cl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值