怎么制作网站教程电商,seo建站优化价格表,泰州做网站的,银川网站制作公司From#xff1a;https://blog.csdn.net/weixin_37947156/article/details/74481758 这篇这要是关于核心组件#xff0c;讲解这些核心组件初始化都做了哪些工作。包括#xff1a;引擎、下载器、调度器、爬虫类、输出处理器 等的初始化。每个核心组件下其实都包含一些小的组件…
Fromhttps://blog.csdn.net/weixin_37947156/article/details/74481758 这篇这要是关于核心组件讲解这些核心组件初始化都做了哪些工作。包括引擎、下载器、调度器、爬虫类、输出处理器 等的初始化。每个核心组件下其实都包含一些小的组件在里面帮助处理某一环节的各种流程。
核心组件初始化核心组件交互流程爬虫类 接着上次代码讲上次的运行入口执行到最后是执行了 Crawler 的 crawl 方法 defer.inlineCallbacksdef crawl(self, *args, **kwargs):assert not self.crawling, Crawling already taking placeself.crawling Truetry:# 到现在才是实例化一个爬虫实例self.spider self._create_spider(*args, **kwargs)# 创建引擎self.engine self._create_engine()# 调用爬虫类的start_requests方法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:# In Python 2 reraising an exception after yield discards# the original traceback (see https://bugs.python.org/issue7563),# so sys.exc_info() workaround is used.# This workaround also works in Python 3, but it is not needed,# and it is slower, so in Python 3 we use native raise.if six.PY2:exc_info sys.exc_info()self.crawling Falseif self.engine is not None:yield self.engine.close()if six.PY2:six.reraise(*exc_info)raise
在这里就交由 scrapy 的 引擎 来处理了。
依次来看爬虫类是如何实例化的上文已讲解过在 Crawler 实例化时会创建 SpiderLoader它会根据用户的配置文件settings.py 找到存放爬虫的位置我们写的爬虫都会放在这里。
然后 SpiderLoader 会扫描这里的所有文件并找到 父类是 scrapy.Spider 爬虫类然后根据爬虫类中的 name 属性在编写爬虫时这个属性是必填的最后生成一个 {spider_name: spider_cls} 的 字典然后根据 scrapy crawl spider_name 命令根据 spider_name 找到对应的爬虫类然后实例化它在这里就是调用了 _create_spider 方法
class Crawler(object):def __init__(self, spidercls, settingsNone):if isinstance(settings, dict) or settings is None:settings Settings(settings)self.spidercls spiderclsself.settings settings.copy()self.spidercls.update_settings(self.settings)d dict(overridden_settings(self.settings))logger.info(Overridden settings: %(settings)r, {settings: d})self.signals SignalManager(self)self.stats load_object(self.settings[STATS_CLASS])(self)handler LogCounterHandler(self, levelself.settings.get(LOG_LEVEL))logging.root.addHandler(handler)if get_scrapy_root_handler() is not None:# scrapy root handler already installed: update it with new settingsinstall_scrapy_root_handler(self.settings)# lambda is assigned to Crawler attribute because this way it is not# garbage collected after leaving __init__ scopeself.__remove_handler lambda: logging.root.removeHandler(handler)self.signals.connect(self.__remove_handler, signals.engine_stopped)lf_cls load_object(self.settings[LOG_FORMATTER])self.logformatter lf_cls.from_crawler(self)self.extensions ExtensionManager.from_crawler(self)self.settings.freeze()self.crawling Falseself.spider Noneself.engine Nonepropertydef spiders(self):if not hasattr(self, _spiders):warnings.warn(Crawler.spiders is deprecated, use CrawlerRunner.spider_loader or instantiate scrapy.spiderloader.SpiderLoader with your settings.,categoryScrapyDeprecationWarning, stacklevel2)self._spiders _get_spider_loader(self.settings.frozencopy())return self._spidersdefer.inlineCallbacksdef crawl(self, *args, **kwargs):assert not self.crawling, Crawling already taking placeself.crawling Truetry:# 到现在才是实例化一个爬虫实例self.spider self._create_spider(*args, **kwargs)# 创建引擎self.engine self._create_engine()# 调用爬虫类的start_requests方法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:# In Python 2 reraising an exception after yield discards# the original traceback (see https://bugs.python.org/issue7563),# so sys.exc_info() workaround is used.# This workaround also works in Python 3, but it is not needed,# and it is slower, so in Python 3 we use native raise.if six.PY2:exc_info sys.exc_info()self.crawling Falseif self.engine is not None:yield self.engine.close()if six.PY2:six.reraise(*exc_info)raisedef _create_spider(self, *args, **kwargs):# 调用类方法from_crawler实例化return self.spidercls.from_crawler(self, *args, **kwargs)def _create_engine(self):return ExecutionEngine(self, lambda _: self.stop())defer.inlineCallbacksdef stop(self):if self.crawling:self.crawling Falseyield defer.maybeDeferred(self.engine.stop)
实例化爬虫比较有意思它不是通过普通的构造方法进行初始化而是调用了类方法 from_crawler 进行的初始化找到scrapy.Spider 类scrapy/spiders/__init__.py
class Spider(object_ref):Base class for scrapy spiders. All spiders must inherit from thisclass.name Nonecustom_settings None # 自定义设置 def __init__(self, nameNone, **kwargs):# spider name 必填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默认是[]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, levellogging.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.,categoryScrapyDeprecationWarning, stacklevel2)assert not hasattr(self, crawler), Spider already bounded to a \crawlerself._set_crawler(crawler)def _set_crawler(self, crawler):self.crawler crawler# 把settings对象赋给spider实例self.settings crawler.settingscrawler.signals.connect(self.close, signals.spider_closed)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 wont 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_filterTrue)def make_requests_from_url(self, url): This method is deprecated. return Request(url, dont_filterTrue)def parse(self, response):raise NotImplementedError({}.parse callback is not defined.format(self.__class__.__name__))classmethoddef update_settings(cls, settings):settings.setdict(cls.custom_settings or {}, priorityspider)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__
在这里可以看到这个类方法其实也是调用了构造方法进行实例化同时也拿到了 settings 配置
再看构造方法干了些什么就是我们平时编写爬虫类时最常用的几个属性name、start_urls、custom_settings。
name在运行爬虫时通过它找到对应的爬虫脚本而使用start_urls定义种子URLcustom_settings从字面意思可以看出爬虫自定义配置会覆盖配置文件的配置项引擎 分析完爬虫类的初始化后还是回到Crawler的crawl方法scrapy/crawler.py 中 Crawler 类 的 crawl 方法 紧接着就是创建 引擎对象也就是 _create_engine 方法这里直接进行了引擎初始化操作看看都发生了什么 在这里能看到进行了核心组件的定义和初始化包括Scheduler、Downloader、Scrapyer其中 Scheduler 只进行了类定义没有实例化。 调度器 调度器初始化发生在引擎的 open_spider 方法中 我们提前来看一下 调度器 的 初始化 完成了哪些工作 调度器的初始化主要做了2件事
实例化请求指纹过滤器用来过滤重复请求可自己重写替换之定义各种不同类型的任务队列优先级任务队列、基于磁盘的任务队列、基于内存的任务队列请求指纹过滤器 先来看请求指纹过滤器是什么在配置文件中定义的默认指纹过滤器是 RFPDupeFilterDUPEFILTER_CLASS scrapy.dupefilters.RFPDupeFilter 请求指纹过滤器初始化时定义了指纹集合这个集合使用内存实现的 set而且可以控制这些指纹是否存入磁盘供下次重复使用。
指纹过滤器的主要职责是过滤重复请求可自定义过滤规则。
在下篇文章中会介绍到每个请求是根据什么规则生成指纹进而实现重复请求过滤逻辑的。 任务队列 调度器默认定义的2种队列类型
基于磁盘的任务队列在配置文件可配置存储路径每次执行后会把队列任务保存到磁盘上基于内存的任务队列每次都在内存中执行下次启动则消失
配置文件默认定义如下 如果用户在配置文件中定义了 JOBDIR那么则每次把任务队列保存在磁盘中下次启动时自动加载。
如果没有定义那么则使用的是内存队列。
细心的你会发现默认定义的这些队列结构都是 后进先出 的什么意思呢
也就是说Scrapy默认的采集规则是深度优先采集
如何改变这种机制变为 广度优先采集 呢那么你可以看一下 scrapy.squeues 模块其中定义了
# 先进先出磁盘队列(pickle序列化)
PickleFifoDiskQueue _serializable_queue(queue.FifoDiskQueue, _pickle_serialize, pickle.loads)# 后进先出磁盘队列(pickle序列化)
PickleLifoDiskQueue _serializable_queue(queue.LifoDiskQueue, _pickle_serialize, pickle.loads)# 先进先出磁盘队列(marshal序列化)
MarshalFifoDiskQueue _serializable_queue(queue.FifoDiskQueue, marshal.dumps, marshal.loads)# 后进先出磁盘队列(marshal序列化)
MarshalLifoDiskQueue _serializable_queue(queue.LifoDiskQueue, marshal.dumps, marshal.loads)# 先进先出内存队列
FifoMemoryQueue queue.FifoMemoryQueue# 后进先出内存队列
LifoMemoryQueue queue.LifoMemoryQueue
你只需要在配置文件中把队列类修改为 先进先出 队列类就可以了有没有发现模块化、组件替代再次发挥威力
如果你想追究这些队列是如何实现的可以参考scrapy作者写的 scrapy/queuelib 模块。 下载器 回头继续看引擎的初始化来看下载器是如何初始化的。
在默认的配置文件 default_settings.py 中下载器配置如下
DOWNLOADER scrapy.core.downloader.Downloader
Downloader 实例化 这个过程主要是初始化了 下载处理器、下载器中间件管理器 以及从配置文件中拿到抓取请求控制相关参数。
下载器 DownloadHandlers 是做什么的
下载器中间件 DownloaderMiddlewareManager 初始化发生了什么 下载处理器 下载处理器在默认的配置文件中是这样配置的 看到这里你应该能明白了说白了就是需下载的资源是什么类型就选用哪一种下载处理器进行网络下载其中最常用的就是http 和 https 对应的处理器。
从这里你也能看出scrapy的架构是非常低耦合的任何涉及到的组件及模块都是可重写和配置的。scrapy提供了基础的服务组件你也可以自己实现其中的某些组件修改配置即可达到替换的目的。
到这里大概就能明白下载处理器的工作就是管理着各种资源对应的下载器在真正发起网络请求时选取对应的下载器进行资源下载。
但是请注意在这个初始化过程中这些下载器是没有被实例化的也就是说在真正发起网络请求时才会进行初始化而且只会初始化一次后面会讲到。 下载器中间件管理器 下面来看下载器中间件 DownloaderMiddlewareManager 初始化同样的这里又调用了类方法 from_crawler 进行初始化DownloaderMiddlewareManager 继承了 MiddlewareManager 类来看它在初始化做了哪些工作
scrapy/core/downloader/middleware.py
from collections import defaultdict, deque
import logging
import pprintfrom scrapy.exceptions import NotConfigured
from scrapy.utils.misc import create_instance, load_object
from scrapy.utils.defer import process_parallel, process_chain, process_chain_bothlogger logging.getLogger(__name__)class MiddlewareManager(object):所有中间件的父类提供中间件公共的方法component_name foo middlewaredef __init__(self, *middlewares):self.middlewares middlewares# 定义中间件方法self.methods defaultdict(deque)for mw in middlewares:self._add_middleware(mw)classmethoddef _get_mwlist_from_settings(cls, settings):# 具体有哪些中间件类子类定义raise NotImplementedErrorclassmethoddef from_settings(cls, settings, crawlerNone):# 调用子类_get_mwlist_from_settings得到所有中间件类的模块mwlist cls._get_mwlist_from_settings(settings)middlewares []enabled []# 依次实例化for clspath in mwlist:try:# 加载这些中间件模块mwcls load_object(clspath)mw create_instance(mwcls, settings, crawler)middlewares.append(mw)enabled.append(clspath)except NotConfigured as e:if e.args:clsname clspath.split(.)[-1]logger.warning(Disabled %(clsname)s: %(eargs)s,{clsname: clsname, eargs: e.args[0]},extra{crawler: crawler})logger.info(Enabled %(componentname)ss:\n%(enabledlist)s,{componentname: cls.component_name,enabledlist: pprint.pformat(enabled)},extra{crawler: crawler})# 调用构造方法return cls(*middlewares)classmethoddef from_crawler(cls, crawler):# 调用 from_settingsreturn cls.from_settings(crawler.settings, crawler)def _add_middleware(self, mw):# 默认定义的子类可覆盖# 如果中间件类有定义open_spider,则加入到methodsif hasattr(mw, open_spider):self.methods[open_spider].append(mw.open_spider)# 如果中间件类有定义close_spider,则加入到methods# methods就是一串中间件的方法链后期会依次调用if hasattr(mw, close_spider):self.methods[close_spider].appendleft(mw.close_spider)def _process_parallel(self, methodname, obj, *args):return process_parallel(self.methods[methodname], obj, *args)def _process_chain(self, methodname, obj, *args):return process_chain(self.methods[methodname], obj, *args)def _process_chain_both(self, cb_methodname, eb_methodname, obj, *args):return process_chain_both(self.methods[cb_methodname], \self.methods[eb_methodname], obj, *args)def open_spider(self, spider):return self._process_parallel(open_spider, spider)def close_spider(self, spider):return self._process_parallel(close_spider, spider)create_instance 函数
def create_instance(objcls, settings, crawler, *args, **kwargs):Construct a class instance using its from_crawler orfrom_settings constructors, if available.At least one of settings and crawler needs to be different fromNone. If settings is None, crawler.settings will be used.If crawler is None, only the from_settings constructor will betried.*args and **kwargs are forwarded to the constructors.Raises ValueError if both settings and crawler are None.if settings is None:if crawler is None:raise ValueError(Specifiy at least one of settings and crawler.)settings crawler.settings# 如果此中间件类定义了from_crawler则调用此方法实例化if crawler and hasattr(objcls, from_crawler):return objcls.from_crawler(crawler, *args, **kwargs)# 如果此中间件类定义了from_settings则调用此方法实例化elif hasattr(objcls, from_settings):return objcls.from_settings(settings, *args, **kwargs)else:# 上面2个方法都没有则直接调用构造实例化return objcls(*args, **kwargs)
DownloaderMiddlewareManager 实例化 下载器中间件管理器 继承了 MiddlewareManager 类然后重写了 _add_middleware 方法为下载行为定义默认的 下载前、下载后、异常时 对应的处理方法。
中间件的职责是什么从这里能大概看出从某个组件流向另一个组件时会经过一系列中间件每个中间件都定义了自己的处理流程相当于一个个管道输入时可以针对数据进行处理然后送达到另一个组件另一个组件处理完逻辑后又经过这一系列中间件这些中间件可再针对这个响应结果进行处理最终输出。 Scraper 下载器实例化完了之后回到引擎的初始化方法中然后是实例化 Scraper在Scrapy源码分析一架构概览中已经大概说到这个类没有在架构图中出现但这个类其实是处于 Engine、Spiders、Pipeline 之间是连通这3个组件的桥梁。
来看它的初始化scrapy/core/scraper.py 爬虫中间件管理器 SpiderMiddlewareManager 初始化 爬虫中间件管理器初始化与之前的下载器中间件管理器类似先是从配置文件中加载了默认的爬虫中间件类然后依次注册爬虫中间件的一系列流程方法。
配置文件中定义的默认的爬虫中间件类如下 这些默认的爬虫中间件职责分别如下
HttpErrorMiddleware会针对响应不是 200 错误进行逻辑处理OffsiteMiddleware如果Spider中定义了 allowed_domains会自动过滤除此之外的域名请求RefererMiddleware追加 Referer 头信息UrlLengthMiddleware控制过滤URL长度超过配置的请求DepthMiddleware过滤超过配置深入的抓取请求
当然你也可以定义自己的爬虫中间件来处理自己需要的逻辑。 Pipeline管理器 爬虫中间件管理器初始化完之后然后就是 Pipeline 组件的初始化默认的 Pipeline 组件是 ItemPipelineManager 可以看到 ItemPipelineManager 也是一个中间件管理器的子类由于它的行为非常类似于中间件但由于功能较为独立所以属于核心组件之一。
从 Scraper 的初始化能够看到它管理着 Spiders 和 Pipeline 相关的交互逻辑。 总结 到这里所有组件引擎、下载器、调度器、爬虫类、输出处理器都依次初始化完成每个核心组件下其实都包含一些小的组件在里面帮助处理某一环节的各种流程。