前面两章介绍了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源码解析的更多相关文章

  1. ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...

  2. 老生常谈系列之Aop--Spring Aop源码解析(一)

    老生常谈系列之Aop--Spring Aop源码解析(一) 前言 上一篇文章老生常谈系列之Aop--Spring Aop原理浅析大概阐述了动态代理的相关知识,并且最后的图给了一个Spring Aop实 ...

  3. 老生常谈系列之Aop--Spring Aop源码解析(二)

    老生常谈系列之Aop--Spring Aop源码解析(二) 前言 上一篇文章老生常谈系列之Aop--Spring Aop源码解析(一)已经介绍完Spring Aop获取advice切面增强方法的逻辑, ...

  4. 小学徒成长系列—StringBuilder & StringBuffer关键源码解析

    在前面的博文<小学徒成长系列—String关键源码解析>和<小学徒进阶系列—JVM对String的处理>中,我们讲到了关于String的常用方法以及JVM对字符串常量Strin ...

  5. 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...

  6. Java 集合系列Stack详细介绍(源码解析)和使用示例

    Stack简介 Stack是栈.它的特性是:先进后出(FILO, First In Last Out). java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现 ...

  7. 第三章 CopyOnWriteArrayList源码解析

    注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...

  8. java容器三:HashMap源码解析

    前言:Map接口 map是一个存储键值对的集合,实现了Map接口的主要类有以下几种 TreeMap:用红黑树实现 HashMap:数组和链表实现 HashTable:与HashMap类似,但是线程安全 ...

  9. 第三章 LinkedList源码解析

    一.对于LinkedList需要掌握的八点内容 LinkedList的创建:即构造器 往LinkedList中添加对象:即add(E)方法 获取LinkedList中的单个对象:即get(int in ...

随机推荐

  1. 菜鸟在ubuntu 16.04下制作am335x的SD卡启动盘反思

    以前只在消费电子平台android和rtos上做软件,每次都是公司的环境现成的,根本不用去想启动加载那部分 的事情,最近在做一个工控机的项目时,接触到了TI的arm335x系列的平台,才发现在嵌入式的 ...

  2. 设置c++中cout输出的字体颜色

    在c++中控制台的默认字体颜色是白色,但是有时我们需要其他颜色,比如用红色提示错误,使用绿色提示计算完成,使用黄色表示警示等等,那么如何设置控制台文字的颜色呢? 一种方法是通过右键控制台进行颜色设置, ...

  3. wxformbuilder在python如何使用

    1使用builder 创建界面并进行布局 2选择python选项 得到相应代码 将代码保存在 一个py文件里面 创建一个新的入口文件 main.py import wx import UIfile c ...

  4. SQL 必知必会·笔记<14>更新和删除数据

    1. 更新数据 基本的UPDATE语句,由三部分组成: 要更新的表 列名和它们的新值 确定要更新那些行的过滤条件 更新单列示例: UPDATE Customers SET cust_email = ' ...

  5. Java线程中的join使用实例

    JDK中解释为 Waits for this thread to die. 等待本线程结束后,下一个线程才可以运行. 实例要求: 现在有T1.T2.T3三个线程,你怎样保证T2在T1执行完后执行,T3 ...

  6. Netty 超时机制及心跳程序实现

    Netty 超时机制的介绍 Netty 的超时类型 IdleState 主要分为: ALL_IDLE : 一段时间内没有数据接收或者发送 READER_IDLE : 一段时间内没有数据接收 WRITE ...

  7. IceCTF 图片隐写

    blue monday 下载一个文件,知道是MIDI文件,后缀改为.mid可以直接播放 一开始以为是两种乐器构成摩斯密码,后来在winhex里看字符发现了每一段的第一个字母恰好有flag的形式,于是提 ...

  8. js-ES6学习笔记-const命令

    1.const声明一个只读的常量.一旦声明,常量的值就不能改变. 2.const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值. 3.const的作用域与l ...

  9. mybatis教程6(逆向工程)

    什么是逆向工程 简单点说,就是通过数据库中的单表,自动生成java代码. Mybatis官方提供了逆向工程,可以针对单表自动生成mybatis代码(mapper.java\mapper.xml\po类 ...

  10. [SHOI2006] 有色图

    Description 给一张 \(n\) 个点的无向完全图,同时还有 \(m\) 种颜色.要求给每条边染色,问有多少种不同的染色方案.两种方案不同当且仅当顶点标号任意重排后不同.\(n\leq 53 ...