Scrapy的核心組件有什么

這篇文章主要講解了“Scrapy的核心組件有什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Scrapy的核心組件有什么”吧!

成都創(chuàng)新互聯(lián)公司是一家朝氣蓬勃的網(wǎng)站建設(shè)公司。公司專注于為企業(yè)提供信息化建設(shè)解決方案。從事網(wǎng)站開(kāi)發(fā),網(wǎng)站制作,網(wǎng)站設(shè)計(jì),網(wǎng)站模板,微信公眾號(hào)開(kāi)發(fā),軟件開(kāi)發(fā),成都小程序開(kāi)發(fā),10余年建站對(duì)成都廣告制作等多個(gè)方面,擁有多年的網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn)。

爬蟲(chóng)類

我們接著上一篇結(jié)束的地方開(kāi)始講起。上次講到 Scrapy 運(yùn)行起來(lái)后,執(zhí)行到最后到了 Crawler 的 crawl 方法,我們來(lái)看這個(gè)方法:

@defer.inlineCallbacks  def crawl(self, *args, **kwargs):      assert not self.crawling, "Crawling already taking place"      self.crawling = True      try:         # 從spiderloader中找到爬蟲(chóng)類 并實(shí)例化爬蟲(chóng)實(shí)例          selfself.spider = self._create_spider(*args, **kwargs)          # 創(chuàng)建引擎          selfself.engine = self._create_engine()          # 調(diào)用爬蟲(chóng)類的start_requests方法 拿到種子URL列表          start_requests = iter(self.spider.start_requests())          # 執(zhí)行引擎的open_spider 并傳入爬蟲(chóng)實(shí)例和初始請(qǐng)求          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

執(zhí)行到這里,我們看到首先創(chuàng)建了爬蟲(chóng)實(shí)例,然后創(chuàng)建了引擎,最后把爬蟲(chóng)交給引擎來(lái)處理了。

在上一篇文章我們也講到,在 Crawler 實(shí)例化時(shí),會(huì)創(chuàng)建 SpiderLoader,它會(huì)根據(jù)我們定義的配置文件 settings.py 找到存放爬蟲(chóng)的位置,我們寫(xiě)的爬蟲(chóng)代碼都在這里。

然后 SpiderLoader 會(huì)掃描這些代碼文件,并找到父類是 scrapy.Spider 爬蟲(chóng)類,然后根據(jù)爬蟲(chóng)類中的 name 屬性(在編寫(xiě)爬蟲(chóng)時(shí),這個(gè)屬性是必填的),生成一個(gè) {spider_name: spider_cls} 的字典,最后根據(jù) scrapy crawl <spider_name> 命令中的 spider_name 找到我們寫(xiě)的爬蟲(chóng)類,然后實(shí)例化它,在這里就是調(diào)用了_create_spider方法:

def _create_spider(self, *args, **kwargs):      # 調(diào)用類方法from_crawler實(shí)例化      return self.spidercls.from_crawler(self, *args, **kwargs)

實(shí)例化爬蟲(chóng)比較有意思,它不是通過(guò)普通的構(gòu)造方法進(jìn)行初始化,而是調(diào)用了類方法 from_crawler 進(jìn)行的初始化,找到 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對(duì)象賦給spider實(shí)例      self.settings = crawler.settings      crawler.signals.connect(self.close, signals.spider_closed)

在這里我們可以看到,這個(gè)類方法其實(shí)也是調(diào)用了構(gòu)造方法,進(jìn)行實(shí)例化,同時(shí)也拿到了 settings 配置,來(lái)看構(gòu)造方法干了些什么?

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)         # 如果沒(méi)有設(shè)置start_urls 默認(rèn)是[]          if not hasattr(self, 'start_urls'):              self.start_urls = []

看到這里是不是很熟悉?這里就是我們平時(shí)編寫(xiě)爬蟲(chóng)類時(shí),最常用的幾個(gè)屬性:name、start_urls、custom_settings:

  •  name:在運(yùn)行爬蟲(chóng)時(shí)通過(guò)它找到我們編寫(xiě)的爬蟲(chóng)類;

  •  start_urls:抓取入口,也可以叫做種子URL;

  •  custom_settings:爬蟲(chóng)自定義配置,會(huì)覆蓋配置文件中的配置項(xiàng);

Scrapy的核心組件有什么

引擎

分析完爬蟲(chóng)類的初始化后,還是回到 Crawler 的 crawl 方法,緊接著就是創(chuàng)建引擎對(duì)象,也就是 _create_engine 方法,看看初始化時(shí)都發(fā)生了什么?

class ExecutionEngine(object):      """引擎"""      def __init__(self, crawler, spider_closed_callback):          self.crawler = crawler          # 這里也把settings配置保存到引擎中          self.settings = crawler.settings          # 信號(hào)          self.signals = crawler.signals          # 日志格式          self.logformatter = crawler.logformatter          self.slot = None          self.spider = None          self.running = False          self.paused = False          # 從settings中找到Scheduler調(diào)度器,找到Scheduler類          self.scheduler_cls = load_object(self.settings['SCHEDULER'])         # 同樣,找到Downloader下載器類          downloader_cls = load_object(self.settings['DOWNLOADER'])          # 實(shí)例化Downloader          self.downloader = downloader_cls(crawler)          # 實(shí)例化Scraper 它是引擎連接爬蟲(chóng)類的橋梁          self.scraper = Scraper(crawler)          self._spider_closed_callback = spider_closed_callback

在這里我們能看到,主要是對(duì)其他幾個(gè)核心組件進(jìn)行定義和初始化,主要包括包括:Scheduler、Downloader、Scrapyer,其中 Scheduler 只進(jìn)行了類定義,沒(méi)有實(shí)例化。

也就是說(shuō),引擎是整個(gè) Scrapy 的核心大腦,它負(fù)責(zé)管理和調(diào)度這些組件,讓這些組件更好地協(xié)調(diào)工作。

Scrapy的核心組件有什么

下面我們依次來(lái)看這幾個(gè)核心組件都是如何初始化的?

調(diào)度器

調(diào)度器初始化發(fā)生在引擎的 open_spider 方法中,我們提前來(lái)看一下調(diào)度器的初始化。

class Scheduler(object):   """調(diào)度器"""      def __init__(self, dupefilter, jobdir=None, dqclass=None, mqclass=None,                   logunser=False, stats=None, pqclass=None):          # 指紋過(guò)濾器          self.df = dupefilter          # 任務(wù)隊(duì)列文件夾          selfself.dqdir = self._dqdir(jobdir)          # 優(yōu)先級(jí)任務(wù)隊(duì)列類          self.pqclass = pqclass          # 磁盤(pán)任務(wù)隊(duì)列類          self.dqclass = dqclass          # 內(nèi)存任務(wù)隊(duì)列類          self.mqclass = mqclass          # 日志是否序列化          self.logunser = logunser          self.stats = stats            @classmethod      def from_crawler(cls, crawler):          settings = crawler.settings          # 從配置文件中獲取指紋過(guò)濾器類          dupefilter_cls = load_object(settings['DUPEFILTER_CLASS'])          # 實(shí)例化指紋過(guò)濾器          dupefilter = dupefilter_cls.from_settings(settings)          # 從配置文件中依次獲取優(yōu)先級(jí)任務(wù)隊(duì)列類、磁盤(pán)隊(duì)列類、內(nèi)存隊(duì)列類          pqclass = load_object(settings['SCHEDULER_PRIORITY_QUEUE'])          dqclass = load_object(settings['SCHEDULER_DISK_QUEUE'])          mqclass = load_object(settings['SCHEDULER_MEMORY_QUEUE'])          # 請(qǐng)求日志序列化開(kāi)關(guān)          logunser = settings.getbool('LOG_UNSERIALIZABLE_REQUESTS', settings.getbool('SCHEDULER_DEBUG'))          return cls(dupefilter, jobdir=job_dir(settings), logunserlogunser=logunser,                     stats=crawler.stats, pqclasspqclass=pqclass, dqclassdqclass=dqclass, mqclassmqclass=mqclass)

可以看到,調(diào)度器的初始化主要做了 2 件事:

  •  實(shí)例化請(qǐng)求指紋過(guò)濾器:主要用來(lái)過(guò)濾重復(fù)請(qǐng)求;

  •  定義不同類型的任務(wù)隊(duì)列:優(yōu)先級(jí)任務(wù)隊(duì)列、基于磁盤(pán)的任務(wù)隊(duì)列、基于內(nèi)存的任務(wù)隊(duì)列;

請(qǐng)求指紋過(guò)濾器又是什么?

在配置文件中,我們可以看到定義的默認(rèn)指紋過(guò)濾器是 RFPDupeFilter:

class RFPDupeFilter(BaseDupeFilter):      """請(qǐng)求指紋過(guò)濾器"""      def __init__(self, path=None, debug=False):          self.file = None          # 指紋集合 使用的是Set 基于內(nèi)存          self.fingerprints = set()          self.logdupes = True          self.debug = debug          self.logger = logging.getLogger(__name__)          # 請(qǐng)求指紋可存入磁盤(pán)          if path:              self.file = open(os.path.join(path, 'requests.seen'), 'a+')              self.file.seek(0)              self.fingerprints.update(x.rstrip() for x in self.file)     @classmethod      def from_settings(cls, settings):          debug = settings.getbool('DUPEFILTER_DEBUG')          return cls(job_dir(settings), debug)

請(qǐng)求指紋過(guò)濾器初始化時(shí),定義了指紋集合,這個(gè)集合使用內(nèi)存實(shí)現(xiàn)的 Set,而且可以控制這些指紋是否存入磁盤(pán)以供下次重復(fù)使用。

也就是說(shuō),指紋過(guò)濾器的主要職責(zé)是:過(guò)濾重復(fù)請(qǐng)求,可自定義過(guò)濾規(guī)則。

在下篇文章中我們會(huì)介紹到,每個(gè)請(qǐng)求是根據(jù)什么規(guī)則生成指紋的,然后是又如何實(shí)現(xiàn)重復(fù)請(qǐng)求過(guò)濾邏輯的,這里我們先知道它的功能即可。

下面來(lái)看調(diào)度器定義的任務(wù)隊(duì)列都有什么作用?

調(diào)度器默認(rèn)定義了 2 種隊(duì)列類型:

  •  基于磁盤(pán)的任務(wù)隊(duì)列:在配置文件可配置存儲(chǔ)路徑,每次執(zhí)行后會(huì)把隊(duì)列任務(wù)保存到磁盤(pán)上;

  •  基于內(nèi)存的任務(wù)隊(duì)列:每次都在內(nèi)存中執(zhí)行,下次啟動(dòng)則消失;

配置文件默認(rèn)定義如下:

# 基于磁盤(pán)的任務(wù)隊(duì)列(后進(jìn)先出)  SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleLifoDiskQueue'  # 基于內(nèi)存的任務(wù)隊(duì)列(后進(jìn)先出)  SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.LifoMemoryQueue'  # 優(yōu)先級(jí)隊(duì)列  SCHEDULER_PRIORITY_QUEUE = 'queuelib.PriorityQueue'

如果我們?cè)谂渲梦募卸x了 JOBDIR 配置項(xiàng),那么每次執(zhí)行爬蟲(chóng)時(shí),都會(huì)把任務(wù)隊(duì)列保存在磁盤(pán)中,下次啟動(dòng)爬蟲(chóng)時(shí)可以重新加載繼續(xù)執(zhí)行我們的任務(wù)。

如果沒(méi)有定義這個(gè)配置項(xiàng),那么默認(rèn)使用的是內(nèi)存隊(duì)列。

細(xì)心的你可能會(huì)發(fā)現(xiàn),默認(rèn)定義的這些隊(duì)列結(jié)構(gòu)都是后進(jìn)先出的,什么意思呢?

也就是在運(yùn)行我們的爬蟲(chóng)代碼時(shí),如果生成一個(gè)抓取任務(wù),放入到任務(wù)隊(duì)列中,那么下次抓取就會(huì)從任務(wù)隊(duì)列中先獲取到這個(gè)任務(wù),優(yōu)先執(zhí)行。

這么實(shí)現(xiàn)意味什么呢?其實(shí)意味著:Scrapy 默認(rèn)的采集規(guī)則是深度優(yōu)先!

如何改變這種機(jī)制,變?yōu)閺V度優(yōu)先采集呢?這時(shí)候我們就要看一下 scrapy.squeues 模塊了,在這里定義了很多種隊(duì)列:

# 先進(jìn)先出磁盤(pán)隊(duì)列(pickle序列化)  PickleFifoDiskQueue = _serializable_queue(queue.FifoDiskQueue, \      _pickle_serialize, pickle.loads)  # 后進(jìn)先出磁盤(pán)隊(duì)列(pickle序列化)  PickleLifoDiskQueue = _serializable_queue(queue.LifoDiskQueue, \      _pickle_serialize, pickle.loads)  # 先進(jìn)先出磁盤(pán)隊(duì)列(marshal序列化)  MarshalFifoDiskQueue = _serializable_queue(queue.FifoDiskQueue, \      marshal.dumps, marshal.loads)  # 后進(jìn)先出磁盤(pán)隊(duì)列(marshal序列化)  MarshalLifoDiskQueue = _serializable_queue(queue.LifoDiskQueue, \      marshal.dumps, marshal.loads)  # 先進(jìn)先出內(nèi)存隊(duì)列  FifoMemoryQueue = queue.FifoMemoryQueue  # 后進(jìn)先出內(nèi)存隊(duì)列  LifoMemoryQueue = queue.LifoMemoryQueue

如果我們想把抓取任務(wù)改為廣度優(yōu)先,我們只需要在配置文件中把隊(duì)列類修改為先進(jìn)先出隊(duì)列類就可以了!從這里我們也可以看出,Scrapy 各個(gè)組件之間的耦合性非常低,每個(gè)模塊都是可自定義的。

如果你想探究這些隊(duì)列是如何實(shí)現(xiàn)的,可以參考 Scrapy 作者寫(xiě)的 scrapy/queuelib 項(xiàng)目,在 Github 上就可以找到,在這里有這些隊(duì)列的具體實(shí)現(xiàn)。

Scrapy的核心組件有什么

下載器

回到引擎的初始化的地方,接下來(lái)我們來(lái)看,下載器是如何初始化的。

在默認(rèn)的配置文件 default_settings.py 中,下載器配置如下:

DOWNLOADER = 'scrapy.core.downloader.Downloader'

我們來(lái)看 Downloader 類的初始化:

class Downloader(object):      """下載器"""      def __init__(self, crawler):          # 同樣的 拿到settings對(duì)象          self.settings = crawler.settings          self.signals = crawler.signals          self.slots = {}          self.active = set()          # 初始化DownloadHandlers          self.handlers = DownloadHandlers(crawler)          # 從配置中獲取設(shè)置的并發(fā)數(shù)          selfself.total_concurrency = self.settings.getint('CONCURRENT_REQUESTS')          # 同一域名并發(fā)數(shù)          selfself.domain_concurrency = self.settings.getint('CONCURRENT_REQUESTS_PER_DOMAIN')          # 同一IP并發(fā)數(shù)          selfself.ip_concurrency = self.settings.getint('CONCURRENT_REQUESTS_PER_IP')          # 隨機(jī)延遲下載時(shí)間          selfself.randomize_delay = self.settings.getbool('RANDOMIZE_DOWNLOAD_DELAY')          # 初始化下載器中間件          self.middleware = DownloaderMiddlewareManager.from_crawler(crawler)          self._slot_gc_loop = task.LoopingCall(self._slot_gc)         self._slot_gc_loop.start(60)

在這個(gè)過(guò)程中,主要是初始化了下載處理器、下載器中間件管理器以及從配置文件中拿到抓取請(qǐng)求控制的相關(guān)參數(shù)。

那么下載處理器是做什么的?下載器中間件又負(fù)責(zé)哪些工作?

先來(lái)看 DownloadHandlers:

class DownloadHandlers(object):      """下載器處理器"""      def __init__(self, crawler):          self._crawler = crawler          self._schemes = {} # 存儲(chǔ)scheme對(duì)應(yīng)的類路徑 后面用于實(shí)例化          self._handlers = {} # 存儲(chǔ)scheme對(duì)應(yīng)的下載器          self._notconfigured = {}          # 從配置中找到DOWNLOAD_HANDLERS_BASE 構(gòu)造下載處理器          # 注意:這里是調(diào)用getwithbase方法  取的是配置中的XXXX_BASE配置          handlers = without_none_values(              crawler.settings.getwithbase('DOWNLOAD_HANDLERS'))          # 存儲(chǔ)scheme對(duì)應(yīng)的類路徑 后面用于實(shí)例化          for scheme, clspath in six.iteritems(handlers):              self._schemes[scheme] = clspath          crawler.signals.connect(self._close, signals.engine_stopped)

下載處理器在默認(rèn)的配置文件中是這樣配置的:

# 用戶可自定義的下載處理器  DOWNLOAD_HANDLERS = {}  # 默認(rèn)的下載處理器  DOWNLOAD_HANDLERS_BASE = {      'file': 'scrapy.core.downloader.handlers.file.FileDownloadHandler',      'http': 'scrapy.core.downloader.handlers.http.HTTPDownloadHandler',      'https': 'scrapy.core.downloader.handlers.http.HTTPDownloadHandler',      's3': 'scrapy.core.downloader.handlers.s3.S3DownloadHandler',      'ftp': 'scrapy.core.downloader.handlers.ftp.FTPDownloadHandler',  }

看到這里你應(yīng)該能明白了,下載處理器會(huì)根據(jù)下載資源的類型,選擇對(duì)應(yīng)的下載器去下載資源。其中我們最常用的就是 http 和 https 對(duì)應(yīng)的處理器。

但是請(qǐng)注意,在這里,這些下載器是沒(méi)有被實(shí)例化的,只有在真正發(fā)起網(wǎng)絡(luò)請(qǐng)求時(shí),才會(huì)進(jìn)行初始化,而且只會(huì)初始化一次,后面文章會(huì)講到。

下面我們來(lái)看下載器中間件 DownloaderMiddlewareManager 初始化過(guò)程,同樣地,這里又調(diào)用了類方法 from_crawler 進(jìn)行初始化,而且 DownloaderMiddlewareManager 繼承了MiddlewareManager 類,來(lái)看它在初始化做了哪些工作:

class MiddlewareManager(object):      """所有中間件的父類,提供中間件公共的方法"""      component_name = 'foo middleware'      @classmethod      def from_crawler(cls, crawler):          # 調(diào)用from_settings          return cls.from_settings(crawler.settings, crawler)       @classmethod      def from_settings(cls, settings, crawler=None):          # 調(diào)用子類_get_mwlist_from_settings得到所有中間件類的模塊          mwlist = cls._get_mwlist_from_settings(settings)          middlewares = []          enabled = []          # 依次實(shí)例化          for clspath in mwlist:              try:                  # 加載這些中間件模塊                  mwcls = load_object(clspath)                  # 如果此中間件類定義了from_crawler 則調(diào)用此方法實(shí)例化                  if crawler and hasattr(mwcls, 'from_crawler'):                      mw = mwcls.from_crawler(crawler)                  # 如果此中間件類定義了from_settings 則調(diào)用此方法實(shí)例化                  elif hasattr(mwcls, 'from_settings'):                      mw = mwcls.from_settings(settings)                  # 上面2個(gè)方法都沒(méi)有,則直接調(diào)用構(gòu)造實(shí)例化                  else:                      mw = mwcls()                  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})          # 調(diào)用構(gòu)造方法          return cls(*middlewares)     @classmethod      def _get_mwlist_from_settings(cls, settings):          # 具體有哪些中間件類,子類定義          raise NotImplementedError      def __init__(self, *middlewares):          self.middlewares = middlewares          # 定義中間件方法          self.methods = defaultdict(list)          for mw in middlewares:              self._add_middleware(mw)        def _add_middleware(self, mw):          # 默認(rèn)定義的 子類可覆蓋          # 如果中間件類有定義open_spider 則加入到methods          if hasattr(mw, 'open_spider'):              self.methods['open_spider'].append(mw.open_spider)          # 如果中間件類有定義close_spider 則加入到methods          # methods就是一串中間件的方法鏈 后期會(huì)依次調(diào)用          if hasattr(mw, 'close_spider'):              self.methods['close_spider'].insert(0, mw.close_spider)

DownloaderMiddlewareManager 實(shí)例化過(guò)程:

class DownloaderMiddlewareManager(MiddlewareManager):   """下載中間件管理器"""      component_name = 'downloader middleware'      @classmethod      def _get_mwlist_from_settings(cls, settings):          # 從配置文件DOWNLOADER_MIDDLEWARES_BASE和DOWNLOADER_MIDDLEWARES獲得所有下載器中間件          return build_component_list(              settings.getwithbase('DOWNLOADER_MIDDLEWARES'))      def _add_middleware(self, mw):          # 定義下載器中間件請(qǐng)求、響應(yīng)、異常一串方法          if hasattr(mw, 'process_request'):              self.methods['process_request'].append(mw.process_request)          if hasattr(mw, 'process_response'):              self.methods['process_response'].insert(0, mw.process_response)          if hasattr(mw, 'process_exception'):              self.methods['process_exception'].insert(0, mw.process_exception)

下載器中間件管理器繼承了 MiddlewareManager 類,然后重寫(xiě)了 _add_middleware 方法,為下載行為定義默認(rèn)的下載前、下載后、異常時(shí)對(duì)應(yīng)的處理方法。

這里我們可以想一下,中間件這么做的好處是什么?

從這里能大概看出,從某個(gè)組件流向另一個(gè)組件時(shí),會(huì)經(jīng)過(guò)一系列中間件,每個(gè)中間件都定義了自己的處理流程,相當(dāng)于一個(gè)個(gè)管道,輸入時(shí)可以針對(duì)數(shù)據(jù)進(jìn)行處理,然后送達(dá)到另一個(gè)組件,另一個(gè)組件處理完邏輯后,又經(jīng)過(guò)這一系列中間件,這些中間件可再針對(duì)這個(gè)響應(yīng)結(jié)果進(jìn)行處理,最終輸出。

Scrapy的核心組件有什么

Scraper

下載器實(shí)例化完了之后,回到引擎的初始化方法中,然后就是實(shí)例化 Scraper,在Scrapy源碼分析(一)架構(gòu)概覽這篇文章中我提到過(guò),這個(gè)類沒(méi)有在架構(gòu)圖中出現(xiàn),但這個(gè)類其實(shí)是處于Engine、Spiders、Pipeline 之間,是連通這三個(gè)組件的橋梁。

我們來(lái)看一下它的初始化過(guò)程:

class Scraper(object):      def __init__(self, crawler):          self.slot = None          # 實(shí)例化爬蟲(chóng)中間件管理器          self.spidermw = SpiderMiddlewareManager.from_crawler(crawler)          # 從配置文件中加載Pipeline處理器類          itemproc_cls = load_object(crawler.settings['ITEM_PROCESSOR'])          # 實(shí)例化Pipeline處理器          self.itemproc = itemproc_cls.from_crawler(crawler)          # 從配置文件中獲取同時(shí)處理輸出的任務(wù)個(gè)數(shù)          self.concurrent_items = crawler.settings.getint('CONCURRENT_ITEMS')          self.crawler = crawler          self.signals = crawler.signals          self.logformatter = crawler.logformatter

Scraper 創(chuàng)建了 SpiderMiddlewareManager,它的初始化過(guò)程:

class SpiderMiddlewareManager(MiddlewareManager):   """爬蟲(chóng)中間件管理器"""      component_name = 'spider middleware'      @classmethod      def _get_mwlist_from_settings(cls, settings):          # 從配置文件中SPIDER_MIDDLEWARES_BASE和SPIDER_MIDDLEWARES獲取默認(rèn)的爬蟲(chóng)中間件類          return build_component_list(settings.getwithbase('SPIDER_MIDDLEWARES'))     def _add_middleware(self, mw):          super(SpiderMiddlewareManager, self)._add_middleware(mw)          # 定義爬蟲(chóng)中間件處理方法          if hasattr(mw, 'process_spider_input'):              self.methods['process_spider_input'].append(mw.process_spider_input)          if hasattr(mw, 'process_spider_output'):              self.methods['process_spider_output'].insert(0, mw.process_spider_output)          if hasattr(mw, 'process_spider_exception'):              self.methods['process_spider_exception'].insert(0, mw.process_spider_exception)          if hasattr(mw, 'process_start_requests'):              self.methods['process_start_requests'].insert(0, mw.process_start_requests)

爬蟲(chóng)中間件管理器初始化與之前的下載器中間件管理器類似,先是從配置文件中加載了默認(rèn)的爬蟲(chóng)中間件類,然后依次注冊(cè)爬蟲(chóng)中間件的一系列流程方法。配置文件中定義的默認(rèn)的爬蟲(chóng)中間件類如下:

SPIDER_MIDDLEWARES_BASE = {   # 默認(rèn)的爬蟲(chóng)中間件類      'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,      'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,      'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,      'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,      'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,  }

這里解釋一下,這些默認(rèn)的爬蟲(chóng)中間件的職責(zé):

  •  HttpErrorMiddleware:針對(duì)非 200 響應(yīng)錯(cuò)誤進(jìn)行邏輯處理;

  •  OffsiteMiddleware:如果Spider中定義了 allowed_domains,會(huì)自動(dòng)過(guò)濾除此之外的域名請(qǐng)求;

  •  RefererMiddleware:追加 Referer 頭信息;

  •  UrlLengthMiddleware:過(guò)濾 URL 長(zhǎng)度超過(guò)限制的請(qǐng)求;

  •  DepthMiddleware:過(guò)濾超過(guò)指定深度的抓取請(qǐng)求;

當(dāng)然,在這里你也可以定義自己的爬蟲(chóng)中間件,來(lái)處理自己所需的邏輯。

爬蟲(chóng)中間件管理器初始化完之后,然后就是 Pipeline 組件的初始化,默認(rèn)的 Pipeline 組件是 ItemPipelineManager:

class ItemPipelineManager(MiddlewareManager):      component_name = 'item pipeline'      @classmethod      def _get_mwlist_from_settings(cls, settings):          # 從配置文件加載ITEM_PIPELINES_BASE和ITEM_PIPELINES類          return build_component_list(settings.getwithbase('ITEM_PIPELINES'))      def _add_middleware(self, pipe):          super(ItemPipelineManager, self)._add_middleware(pipe)          # 定義默認(rèn)的pipeline處理邏輯          if hasattr(pipe, 'process_item'):              self.methods['process_item'].append(pipe.process_item)     def process_item(self, item, spider):          # 依次調(diào)用所有子類的process_item方法          return self._process_chain('process_item', item, spider)

我們可以看到 ItemPipelineManager 也是中間件管理器的一個(gè)子類,由于它的行為非常類似于中間件,但由于功能較為獨(dú)立,所以屬于核心組件之一。

從 Scraper 的初始化過(guò)程我們可以看出,它管理著 Spiders 和 Pipeline 相關(guān)的數(shù)據(jù)交互。

感謝各位的閱讀,以上就是“Scrapy的核心組件有什么”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Scrapy的核心組件有什么這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

本文題目:Scrapy的核心組件有什么
網(wǎng)站地址:http://bm7419.com/article26/pcoocg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、全網(wǎng)營(yíng)銷推廣服務(wù)器托管、靜態(tài)網(wǎng)站、手機(jī)網(wǎng)站建設(shè)、做網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化