scrapy框架中Spider源码解析

一、scrapy架构

在讲解spider类之前,我们先来了解下scrapy这个框架的整体架构
请看下面scrapy工作流程图

1.scrapy引擎(Scrapy Engine)

引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。

2.调度器(Scheduler)

调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎

3.下载器(Downloader)

下载器负责获取页面数据并提供给引擎,而后提供给spider。

4.蜘蛛(Spiders)

Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。

5.管道(Item Pipeline)

Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)

6.下载器中间件(Downloader Middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response(也包括引擎传递给下载器的Request)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

7.spider中间件(Spider Middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

scrapy架构工作流程(数据流向)

Scrapy中的数据流由执行引擎控制,其过程如下:

1.引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
2. 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
3. 引擎向调度器请求下一个要爬取的URL。
4. 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
6. 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
7. Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
8. 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
(从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。

二、spider源码解析

Spider是最基本的类,所有爬虫必须继承这个类。
Spider类主要用到的函数及调用顺序为:
(1)init()方法: 初始化爬虫名字和start_urls列表。

注:这里爬虫名称是必须的,而且必须是唯一的
def __init__(self, name=None, **kwargs):if name is not None:self.name = nameelif not g`在这里插入代码片`etattr(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 = []

(2)start_requests()方法:spider发起请求时会调用make_requests_from_url()生成Requests对象交给Scrapy下载并返回Response对象交给解析函数处理。

注:start_requests()方法只调用一次
def start_requests(self):cls = self.__class__if method_is_overridden(cls, Spider, 'make_requests_from_url'):warnings.warn("Spider.make_requests_from_url method is deprecated; it ""won't be called in future Scrapy releases. Please ""override Spider.start_requests method instead (see %s.%s)." % (cls.__module__, cls.__name__),)for url in self.start_urls:yield self.make_requests_from_url(url)else:for url in self.start_urls:yield Request(url, dont_filter=True)def make_requests_from_url(self, url):""" This method is deprecated. """return Request(url, dont_filter=True)

(3)parse()方法:解析下载器返回的response,并返回Item或Requests(需指定回调函数)。Item传给Item pipline进行数据的持久化存储,Requests交由Scrapy下载,并由指定的回调函数处理,一直进行循环,直到处理完所有的数据为止。

重点:这个内容需要我们自己去写。parse()是默认的Request对象回调函数,解析返回的response对象, 注意回调函数的写法,是函数地址(callback=parse或者callback=None)。
    def parse(self, response):raise NotImplementedError

三、spider全部源码展示及详情解析

"""
Base class for Scrapy spiders
See documentation in docs/topics/spiders.rst
"""
import logging
import warningsfrom scrapy import signals
from scrapy.http import Request
from scrapy.utils.trackref import object_ref
from scrapy.utils.url import url_is_from_spider
from scrapy.utils.deprecate import create_deprecated_class
from scrapy.exceptions import ScrapyDeprecationWarning
from scrapy.utils.deprecate import method_is_overridden#所有爬虫的基类,用户定义的爬虫必须从这个类继承
class Spider(object_ref):"""Base class for scrapy spiders. All spiders must inherit from thisclass."""#1、定义spider名字的字符串。spider的名字定义了Scrapy如何定位(并初始化)spider,所以其必须是唯一的。#2、name是spider最重要的属性,而且是必须的。一般做法是以该网站的域名来命名spider。例如我们在爬取豆瓣读书爬虫时使用‘name = "douban_book_spider"’  name = Nonecustom_settings = None#初始化爬虫名字和start_urls列表。上面已经提到。def __init__(self, name=None, **kwargs):#初始化爬虫名字if name is not None:self.name = nameelif not getattr(self, 'name', None):raise ValueError("%s must have a name" % type(self).__name__)self.__dict__.update(kwargs)#初始化start_urls列表,当没有指定的URL时,spider将从该列表中开始进行爬取。 因此,第一个被获取到的页面的URL将是该列表之一,后续的URL将会从获取到的数据中提取。  if not hasattr(self, 'start_urls'):self.start_urls = []@propertydef 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 levelThis helper wraps a log call to the logger within the spider, but youcan use it directly (e.g. Spider.logger.info('msg')) or use any otherPython logger too."""self.logger.log(level, message, **kw)@classmethoddef from_crawler(cls, crawler, *args, **kwargs):spider = cls(*args, **kwargs)spider._set_crawler(crawler)return spiderdef 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=2)assert not hasattr(self, 'crawler'), "Spider already bounded to a " \"crawler"self._set_crawler(crawler)def _set_crawler(self, crawler):self.crawler = crawlerself.settings = crawler.settingscrawler.signals.connect(self.close, signals.spider_closed)#该方法将读取start_urls列表内的地址,为每一个地址生成一个Request对象,并返回这些对象的迭代器。#注意:该方法只会调用一次。def start_requests(self):cls = self.__class__if method_is_overridden(cls, Spider, 'make_requests_from_url'):warnings.warn("Spider.make_requests_from_url method is deprecated; it ""won't be called in future Scrapy releases. Please ""override Spider.start_requests method instead (see %s.%s)." % (cls.__module__, cls.__name__),)for url in self.start_urls:yield self.make_requests_from_url(url)else:for url in self.start_urls:yield Request(url, dont_filter=True)#1、start_requests()中调用,实际生成Request的函数。#2、Request对象默认的回调函数为parse(),提交的方式为get。def make_requests_from_url(self, url):""" This method is deprecated. """return Request(url, dont_filter=True)#默认的Request对象回调函数,处理返回的response。  #生成Item或者Request对象。这个类需要我们自己去实现。def parse(self, response):raise NotImplementedError@classmethoddef update_settings(cls, settings):settings.setdict(cls.custom_settings or {}, priority='spider')@classmethoddef handles_request(cls, request):return url_is_from_spider(request.url, cls)@staticmethoddef 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__BaseSpider = create_deprecated_class('BaseSpider', Spider)class ObsoleteClass(object):def __init__(self, message):self.message = messagedef __getattr__(self, name):raise AttributeError(self.message)spiders = ObsoleteClass('"from scrapy.spider import spiders" no longer works - use ''"from scrapy.spiderloader import SpiderLoader" and instantiate ''it with your project settings"'
)# Top-level imports
from scrapy.spiders.crawl import CrawlSpider, Rule
from scrapy.spiders.feed import XMLFeedSpider, CSVFeedSpider
from scrapy.spiders.sitemap import SitemapSpider作者:小怪聊职场
链接:https://www.jianshu.com/p/d492adf17312
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

注:某些内容参考自csdn部分大佬博客

scrapy框架中Spider源码解析相关推荐

  1. 轻量级Rpc框架设计--motan源码解析六:client端服务发现

    一, Client端初始化工作 client端通过RefererConfigBean类实现InitializingBean接口的afterPropertiesSet方法, 进行下面三项检查配置工作: ...

  2. 红黑树分析与JDK8中HashMap源码解析

    红黑树分析与JDK8中HashMap源码解析 BST O(1), O(n), O(logn), O(nlogn) 的区别 红黑树-RBTree 插入数据 HashMap中红黑树的插入操作 HashMa ...

  3. php的lumen框架,Lumen框架“服务容器”源码解析

    1.服务容器 "服务容器"是Lumen框架整个系统功能调度配置的核心,它提供了整个框架运行过程中的一系列服务."服务容器"就是提供服务(服务可以理解为系统运行中 ...

  4. Volley框架使用及源码解析

    1. Volley特点 (1) 特别适合数据量小,通信频繁的网络操作. (2) 扩展性强.Volley 中大多是基于接口的设计,可根据需要自行定制. (3) 一定程度符合 Http 规范,包括返回 R ...

  5. 最全最简单scrapy框架搭建(附源码案例)

    最近在做项目中,需要网页的大批数据,查询数据是一项体力劳动,原本的我 然而,奋斗了一天的我查到的数据却寥寥无几,后来的我是这样的 作为一个cv工程师,复制粘贴原本是一件很快乐的事情但是它缺给了我无尽的 ...

  6. Android 网络框架之Retrofit源码解析,flutter边框特效

    Retrofit的构建使用了建造者模式,这个模式的优点就是可以构造复杂的对象,方便扩展,并且看起来代码比较简洁,美观: 在开始之前,我们先来看一下Retrofit的成员变量: 这里的变量并不是很多,我 ...

  7. JDK8中ConcurrentHashMap源码解析

    在介绍ConcurrentHashMap源码之前,首先需要了解以下几个知识 1.JDK1.8中ConcurrentHashMap的基本结构 2.并发编程的三个概念:可见性,原子性,有序性 3.CAS( ...

  8. PX4中vtol_att_control 源码解析

    px4中vtol姿态控制源码分析 /src/modules/vtol_att_control/文件夹中包含vtol_att_control_main.vtol_type.standard/tailsi ...

  9. java中acquire()_Java高并发系列之AQS中acquire源码解析

    我们知道,AQS中最重要的两个方法就是acquire和release方法.我们本文来走读走读acquire的源码. 首先,tryAcquire是需要子类具体去实现,其作用就是设置state的值,如果设 ...

  10. java compareto源码_java中compareTo源码解析(String类型)

    官网API(JDK13): 先上结论: 1.计算compareTo两边字符串的总长度(length()) 2.求出总长度中的最小值 3.如果长度相等,比较ASCII值相同,返回0 4.如果长度不相同, ...

最新文章

  1. 主席树 ---- CF 1422F. Boring Queries(由离线推出在线如何求的 ,求解多次询问的区间LCM)
  2. 我摊牌了,大厂面试Linux就这5个问题
  3. php销毁three.js量,javascript – ThreeJS:从场景中删除对象
  4. android 页卡切换实现,TabLayout+ViewPager实现选项卡切换效果
  5. 使用 Benchmark.NET 测试代码性能
  6. bs 程序用户个性化设置保存_想更改win10设置,这12种打开方法你不可不知,方便又快捷...
  7. Autosar诊断——故障诊断方案设计
  8. fedora linux搜狗输入法,GitHub - Hello-Linux/fedora-Sougou-Pinyin: fedora 搜狗拼音,安装超简单,各种精美皮肤!...
  9. 一会通一会不通 一台设备ping_Linux刚开机能ping通网关,一会就ping不通了
  10. vue如何调用高德地图
  11. ES6 import命令和import()函数区别
  12. 已解决-安装CentOS 7时No Caching mode page found和Assuming drive cache:write through报错问题
  13. java单链表反转(头插法)详解
  14. 【021】基于springboot的党务管理系统(含管理员、用户党支部三种身份(附源码数据库)
  15. 国外邮箱安全性排名,国外邮箱哪个安全好用?
  16. Ali-Perseus(擎天):统一深度学习分布式通信框架 [弹性人工智能]
  17. 利用adb设置安卓http代理
  18. StrStrI 与 strstr
  19. 2018蓝桥杯C/C++ A组C组题目汇总
  20. Android项目之视频播放器

热门文章

  1. springcloud中feign调用的权限认证
  2. c语言学习记录|输入三角形三边的长度,计算三角形周长和面积。
  3. TMS320C6748_ECAP_APWM
  4. Linux应该怎么快速学习?首推这份全网爆火的“Linux速成笔记”,阿里架构师都在用它!
  5. Idea 中最常用的10款插件(提高开发效率),一定要学会使用!
  6. xp系统怎么创建服务器新用户,xp系统ftp服务器创建用户
  7. 初学C语言——三位数倒序
  8. es文件浏览器smb服务器,ES文件浏览器怎么连接电脑SMB,可以参考这篇文章
  9. 移动端Touch (触摸)事件
  10. UDAL 分布式数据库介绍(1)