scrapy系列(三)——基础spider源码解析
前面两章介绍了scrapy的安装和项目的新建,那么这一章就讲讲spider吧。
scrapy有个命令是runspider, 这个命令的作用就是将一个spider当做一个python文件去执行,而不用创建一个完整的项目。可以说是最简单的一个爬虫项目了,只有一个文件,这也体现出了spider对于scrapy的重要性,item和pipline可有可无,settings等也可以使用默认的,可是spider必须自己构造。而我们写爬虫的时候大部分时间和精力也是耗费在这里,所以spider的重要性就不言而喻了。
这次教程将结合官方文档和源码一起来讲解,希望大家能够喜欢。
首先看看官方文档对于spider的介绍:

最上面一段就不解释了,就是介绍spider的作用和功能。对于spider来说主要经历如下几件事:
1.根据url生成Request并指定回调方法处理Response。第一个Request是通过start_requests()产生的,该方法下面会讲到。
2. 在回调方法中,解析页面的Response,返回Item实例或者Request实例,或者这两种实例的可迭代对象。
3.在回调方法中,通常使用Selectors(也可以使用BeautifulSoup,lxml等)来提取数据。
4.最后spider会return item给Pipline完成数据的清洗,持久化等操作。
scrapy为我们提供了几款基础的spider,我们需要继承这些来实现自己的spider。我们接下来就根据资料及源码来了解它们。
class scrapy.spiders.Spider 下面是它的源码:
class Spider(object_ref):
"""Base class for scrapy spiders. All spiders must inherit from this
class.
""" name = None
custom_settings = None def __init__(self, name=None, **kwargs):
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)
if not hasattr(self, 'start_urls'):
self.start_urls = [] @property
def logger(self):
logger = logging.getLogger(self.name)
return logging.LoggerAdapter(logger, {'spider': self}) def log(self, message, level=logging.DEBUG, **kw):
"""Log the given message at the given log level This helper wraps a log call to the logger within the spider, but you
can use it directly (e.g. Spider.logger.info('msg')) or use any other
Python logger too.
"""
self.logger.log(level, message, **kw) @classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = cls(*args, **kwargs)
spider._set_crawler(crawler)
return spider def set_crawler(self, crawler):
warnings.warn("set_crawler is deprecated, instantiate and bound the "
"spider to this crawler with from_crawler method "
"instead.",
category=ScrapyDeprecationWarning, stacklevel=)
assert not hasattr(self, 'crawler'), "Spider already bounded to a " \
"crawler"
self._set_crawler(crawler) def _set_crawler(self, crawler):
self.crawler = crawler
self.settings = crawler.settings
crawler.signals.connect(self.close, signals.spider_closed) def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url) def make_requests_from_url(self, url):
return Request(url, dont_filter=True) def parse(self, response):
raise NotImplementedError @classmethod
def update_settings(cls, settings):
settings.setdict(cls.custom_settings or {}, priority='spider') @classmethod
def handles_request(cls, request):
return url_is_from_spider(request.url, cls) @staticmethod
def close(spider, reason):
closed = getattr(spider, 'closed', None)
if callable(closed):
return closed(reason) def __str__(self):
return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self)) __repr__ = __str__
该类的基类是object_ref,其定义如下图所示:

从源码中可以看到这个类的子类的实例都会记录它本身的存活状况,这个作用会在以后讲解,目前用不到。
类scrapy.spiders.Spider 是最简单的spider,所有的spider包括自己定义的和scrapy提供的都会继承它。它只是提供最基本的特性,也是我最常用的spider类。
name:
这个属性是字符串变量,是这个类的名称,代码会通过它来定位spider,所以它必须唯一,它是spider最重要的属性。回头看看源码中__init__的定义,可以发现这个属性是可以修改的,如果不喜欢或者有需要重命名spider的name,可以在启动的时候传参修改name属性。
allowed_domains:
这个属性是一个列表,里面记载了允许采集的网站的域名,该值如果没定义或者为空时表示所有的域名都不进行过滤操作。如果url的域名不在这个变量中,那么这个url将不会被处理。不想使用域名过滤功能时可以在settings中注释掉OffsiteMiddleware, 个人不建议这么做。
start_urls:
这个属性是一个列表或者元组,其作用是存放起始urls,相当于这次任务的种子。使用默认模板创建spider时,该值是个元组,创建元组并且只有一个元素时需要在元素后面添加“,”来消除歧义,不然会报错:“ValueError: Missing scheme in request url: h”。这边经常有人出错,为了避免这个错误可以根据上一章内容,将模板中start_urls的值设置为列表。
custom_settings:
这个属性值是一个字典,存放settings键值对,用于覆盖项目中的settings.py的值,可以做到在一个项目中的不同spider可以有不同的配置。不过这个值要慎用,有些settings的值覆盖也没有起作用,eg:“LOG_FILE”。如果想每个spider都有自己的log文件的话就不能这么做。因为日志操作在这个方法执行之前,那么无论怎么改都改不了之前的行为。不过这个问题scrapy研发团队已经注意到了,相信不久的将来会进行处理的。
crawler:
这个值从源码可以看出来自于方法from_crawler()。该值是一个Crawler 实例, 其作用后面的教程会讲解,这边就不细说了。
settings:
这个值也是来自于方法from_crawler()。是一个Settings 实例,这个后面也会细说,稍安勿躁哈。
logger:
顾名思义,记录日志用的,也是后面讲,耐心等候哈。
from_crawler:
这是一个类方法,scrapy创建spider的时候会调用。调用位置在crawler.py 的类Crawler中,源码可以自己去看看,就不带大家看了。这个方法的源码在上面,我们可以看到,在实例化这个spider以后,这个实例才有的settings和crawler属性,所以在__init__方法中是没法访问这俩属性的。如果非要在__init__方法中使用相关属性,那么只能重写该方法,大家可以尝试写写。
start_requests():
这个方法必须返回一个可迭代对象,切记!!!!上面就有源码很简单,就不细说了。如果想对属性start_urls做一些操作(增删改),并希望结果作为种子url去采集网站的时候,可以重写这个方法来实现。有了这个方法,甚至都不用在代码中定义start_urls。比如我们想要读取持久化的url执行采集操作,那么就没必要转存进start_urls里面,可以直接请求这些urls。当种子urls需要post请求的话,也需要重写该方法。
make_requests_from_url(url):
这个方法顾名思义,要是还不懂就看看上面的源码。这里只说一点,因为这里的Request初始化没有回调方法,就是默认采用parse方法作为回调。另外这里的dont_filter值为True,这个值的作用是该url不会被过滤,至于具体细节请听下回分解。
parse(self, response):
这个方法对于有经验的同学来说再熟悉不过了,我就简单的说说。这个方法作为默认回调方法,Request没有指定回调方法的时候会调用它,这个回调方法和别的回调方法一样返回值只能是Request, 字典和item对象,或者它们的可迭代对象。
log(message[, level, component]):
这个方法是对logger的包装,看看源码就好,没什么什么可说的。
closed(reason):
当这个spider结束时这个方法会被调用,参数是一个字符串,是结束的原因。这种用法以后会介绍,这里只需记住,想在spider结束时做一些操作时可以写在这里。
scrapy基础spider算是介绍完了,有兴趣的小伙伴可以对照一个好的实例来看这个文章,或许帮助更大。如果有什么疑问,大家可以在评论区讨论,共同进步。
scrapy系列(三)——基础spider源码解析的更多相关文章
- ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>
本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...
- 老生常谈系列之Aop--Spring Aop源码解析(一)
老生常谈系列之Aop--Spring Aop源码解析(一) 前言 上一篇文章老生常谈系列之Aop--Spring Aop原理浅析大概阐述了动态代理的相关知识,并且最后的图给了一个Spring Aop实 ...
- 老生常谈系列之Aop--Spring Aop源码解析(二)
老生常谈系列之Aop--Spring Aop源码解析(二) 前言 上一篇文章老生常谈系列之Aop--Spring Aop源码解析(一)已经介绍完Spring Aop获取advice切面增强方法的逻辑, ...
- 小学徒成长系列—StringBuilder & StringBuffer关键源码解析
在前面的博文<小学徒成长系列—String关键源码解析>和<小学徒进阶系列—JVM对String的处理>中,我们讲到了关于String的常用方法以及JVM对字符串常量Strin ...
- 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...
- Java 集合系列Stack详细介绍(源码解析)和使用示例
Stack简介 Stack是栈.它的特性是:先进后出(FILO, First In Last Out). java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现 ...
- 第三章 CopyOnWriteArrayList源码解析
注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...
- java容器三:HashMap源码解析
前言:Map接口 map是一个存储键值对的集合,实现了Map接口的主要类有以下几种 TreeMap:用红黑树实现 HashMap:数组和链表实现 HashTable:与HashMap类似,但是线程安全 ...
- 第三章 LinkedList源码解析
一.对于LinkedList需要掌握的八点内容 LinkedList的创建:即构造器 往LinkedList中添加对象:即add(E)方法 获取LinkedList中的单个对象:即get(int in ...
随机推荐
- typeof 与instanceof
函数原型链: 2.typeof获取到的是产生该对象的根源object, instanceof获取的是最终产生该对象的父级构造函数 <script> var arr=[1,2,3,4,&qu ...
- django配置celery
官网详尽的django结合celery的配置步骤 在django项目settings.py所在的目录中新建一个celery.py,内容如下 from __future__ import absolut ...
- [源码]Delphi源码免杀之函数动态调用 实现免杀的下载者
[免杀]Delphi源码免杀之函数动态调用 实现免杀的下载者 2013-12-30 23:44:21 来源:K8拉登哥哥's Blog 自己编译这份代码看看 过N多杀软 没什么技 ...
- 开启python学习之路
新生入学这一周来,基本都在看python从入门到精通,边看书边敲代码,简单的几行代码,巩固学到的知识,像当初学习各类编程语言一样,从最初开发环境的搭建,数据类型等,学习中做好笔记,然后学会运用. 学习 ...
- Android 源码分析01_AsyncTask
[参考文献] http://blog.csdn.net/singwhatiwanna/article/details/17596225 /* * Copyright (C) 2008 The Andr ...
- TCP/IP 笔记 - 防火墙和网络地址转换
防火墙是位于内部网和外部网之间的屏障,是系统的第一套防线,作用是防止非法用户的进入. 网络地址转换是一种IP数据包通过路由器或防火墙时通过重写来源IP地址或目的地址的技术,可以用来隐藏或保护内部网络, ...
- SSE图像算法优化系列五:超高速指数模糊算法的实现和优化(10000*10000在100ms左右实现)。
今天我们来花点时间再次谈谈一个模糊算法,一个超级简单但是又超级牛逼的算法,无论在效果上还是速度上都可以和Boxblur, stackblur或者是Gaussblur想媲美,效果上,比Boxblur来的 ...
- C# 中集合类型需要按多个条件排序
在 C# (.net 3.5 之后) 中集合是可以通过 OrderBy() 和 OrderByDescending()方法来进行排序的,如果需要集合中的元素是对象,还可以通过 Lambda表达式进行按 ...
- 【Spark笔记】Windows10 本地搭建单机版Spark开发环境
0x00 环境及软件 1.系统环境 OS:Windows10_x64 专业版 2.所需软件或工具 JDK1.8.0_131 spark-2.3.0-bin-hadoop2.7.tgz hadoop-2 ...
- python 浅析模块,包及其相关用法
今天买了一本关于模块的书,说实话,模块真的太多了,小编许多也不知道,要是把模块全讲完,可能得出本书了,所以小编在自己有限的能力范围内在这里浅析一下自己的见解,同时讲讲几个常用的模块. 这里是2018. ...