精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Scrapy源碼剖析:Scrapy有哪些核心組件?

開發 后端
這篇文章,我們就來進一步剖析一下,Scrapy 有哪些核心組件?以及它們主要負責了哪些工作?這些組件為了完成這些功能,內部又是如何實現的。

 在上一篇文章:Scrapy源碼剖析:Scrapy是如何運行起來的?我們主要剖析了 Scrapy 是如何運行起來的核心邏輯,也就是在真正執行抓取任務之前,Scrapy 都做了哪些工作。

這篇文章,我們就來進一步剖析一下,Scrapy 有哪些核心組件?以及它們主要負責了哪些工作?這些組件為了完成這些功能,內部又是如何實現的。

爬蟲類

我們接著上一篇結束的地方開始講起。上次講到 Scrapy 運行起來后,執行到最后到了 Crawler 的 crawl 方法,我們來看這個方法: 

  1. @defer.inlineCallbacks  
  2. def crawl(self, *args, **kwargs):  
  3.     assert not self.crawling, "Crawling already taking place"  
  4.     self.crawling = True  
  5.     try: 
  6.         # 從spiderloader中找到爬蟲類 并實例化爬蟲實例  
  7.         selfself.spider = self._create_spider(*args, **kwargs)  
  8.         # 創建引擎  
  9.         selfself.engine = self._create_engine()  
  10.         # 調用爬蟲類的start_requests方法 拿到種子URL列表  
  11.         start_requests = iter(self.spider.start_requests())  
  12.         # 執行引擎的open_spider 并傳入爬蟲實例和初始請求  
  13.         yield self.engine.open_spider(self.spider, start_requests)  
  14.         yield defer.maybeDeferred(self.engine.start)  
  15.     except Exception:  
  16.         if six.PY2:  
  17.             exc_info = sys.exc_info()  
  18.         self.crawling = False  
  19.         if self.engine is not None:  
  20.             yield self.engine.close()  
  21.         if six.PY2:  
  22.             six.reraise(*exc_info)  
  23.         raise 

執行到這里,我們看到首先創建了爬蟲實例,然后創建了引擎,最后把爬蟲交給引擎來處理了。

在上一篇文章我們也講到,在 Crawler 實例化時,會創建 SpiderLoader,它會根據我們定義的配置文件 settings.py 找到存放爬蟲的位置,我們寫的爬蟲代碼都在這里。

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

  1. def _create_spider(self, *args, **kwargs):  
  2.     # 調用類方法from_crawler實例化  
  3.     return self.spidercls.from_crawler(self, *args, **kwargs) 

實例化爬蟲比較有意思,它不是通過普通的構造方法進行初始化,而是調用了類方法 from_crawler 進行的初始化,找到 scrapy.Spider 類: 

  1. @classmethod  
  2. def from_crawler(cls, crawler, *args, **kwargs):  
  3.     spider = cls(*args, **kwargs)  
  4.     spider._set_crawler(crawler)  
  5.     return spider      
  6. def _set_crawler(self, crawler):  
  7.     self.crawler = crawler  
  8.     # 把settings對象賦給spider實例  
  9.     self.settings = crawler.settings  
  10.     crawler.signals.connect(self.close, signals.spider_closed) 

在這里我們可以看到,這個類方法其實也是調用了構造方法,進行實例化,同時也拿到了 settings 配置,來看構造方法干了些什么? 

  1. class Spider(object_ref):  
  2.     name = None 
  3.     custom_settings = None  
  4.     def __init__(self, name=None, **kwargs):  
  5.         # name必填  
  6.         if name is not None:  
  7.             self.name = name  
  8.         elif not getattr(self, 'name', None):  
  9.             raise ValueError("%s must have a name" % type(self).__name__)  
  10.         self.__dict__.update(kwargs) 
  11.         # 如果沒有設置start_urls 默認是[]  
  12.         if not hasattr(self, 'start_urls'):  
  13.             self.start_urls = [] 

看到這里是不是很熟悉?這里就是我們平時編寫爬蟲類時,最常用的幾個屬性:name、start_urls、custom_settings:

  •  name:在運行爬蟲時通過它找到我們編寫的爬蟲類;
  •  start_urls:抓取入口,也可以叫做種子URL;
  •  custom_settings:爬蟲自定義配置,會覆蓋配置文件中的配置項;

引擎

分析完爬蟲類的初始化后,還是回到 Crawler 的 crawl 方法,緊接著就是創建引擎對象,也就是 _create_engine 方法,看看初始化時都發生了什么? 

  1. class ExecutionEngine(object):  
  2.     """引擎"""  
  3.     def __init__(self, crawler, spider_closed_callback):  
  4.         self.crawler = crawler  
  5.         # 這里也把settings配置保存到引擎中  
  6.         self.settings = crawler.settings  
  7.         # 信號  
  8.         self.signals = crawler.signals  
  9.         # 日志格式  
  10.         self.logformatter = crawler.logformatter  
  11.         self.slot = None  
  12.         self.spider = None  
  13.         self.running = False  
  14.         self.paused = False  
  15.         # 從settings中找到Scheduler調度器,找到Scheduler類  
  16.         self.scheduler_cls = load_object(self.settings['SCHEDULER']) 
  17.         # 同樣,找到Downloader下載器類  
  18.         downloader_cls = load_object(self.settings['DOWNLOADER'])  
  19.         # 實例化Downloader  
  20.         self.downloader = downloader_cls(crawler)  
  21.         # 實例化Scraper 它是引擎連接爬蟲類的橋梁  
  22.         self.scraper = Scraper(crawler)  
  23.         self._spider_closed_callback = spider_closed_callback 

在這里我們能看到,主要是對其他幾個核心組件進行定義和初始化,主要包括包括:Scheduler、Downloader、Scrapyer,其中 Scheduler 只進行了類定義,沒有實例化。

也就是說,引擎是整個 Scrapy 的核心大腦,它負責管理和調度這些組件,讓這些組件更好地協調工作。

下面我們依次來看這幾個核心組件都是如何初始化的?

調度器

調度器初始化發生在引擎的 open_spider 方法中,我們提前來看一下調度器的初始化。 

  1. class Scheduler(object):  
  2.  """調度器"""  
  3.     def __init__(self, dupefilter, jobdir=Nonedqclass=Nonemqclass=None 
  4.                  logunser=Falsestats=Nonepqclass=None):  
  5.         # 指紋過濾器  
  6.         self.df = dupefilter  
  7.         # 任務隊列文件夾  
  8.         selfself.dqdir = self._dqdir(jobdir)  
  9.         # 優先級任務隊列類  
  10.         self.pqclass = pqclass  
  11.         # 磁盤任務隊列類  
  12.         self.dqclass = dqclass  
  13.         # 內存任務隊列類  
  14.         self.mqclass = mqclass  
  15.         # 日志是否序列化  
  16.         self.logunser = logunser  
  17.         self.stats = stats       
  18.      @classmethod  
  19.     def from_crawler(cls, crawler):  
  20.         settings = crawler.settings  
  21.         # 從配置文件中獲取指紋過濾器類  
  22.         dupefilter_cls = load_object(settings['DUPEFILTER_CLASS'])  
  23.         # 實例化指紋過濾器  
  24.         dupefilter = dupefilter_cls.from_settings(settings)  
  25.         # 從配置文件中依次獲取優先級任務隊列類、磁盤隊列類、內存隊列類  
  26.         pqclass = load_object(settings['SCHEDULER_PRIORITY_QUEUE'])  
  27.         dqclass = load_object(settings['SCHEDULER_DISK_QUEUE'])  
  28.         mqclass = load_object(settings['SCHEDULER_MEMORY_QUEUE'])  
  29.         # 請求日志序列化開關  
  30.         logunser = settings.getbool('LOG_UNSERIALIZABLE_REQUESTS', settings.getbool('SCHEDULER_DEBUG'))  
  31.         return cls(dupefilter, jobdir=job_dir(settings), logunserlogunser=logunser,  
  32.                    stats=crawler.stats, pqclasspqclass=pqclass, dqclassdqclass=dqclass, mqclassmqclass=mqclass) 

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

  •  實例化請求指紋過濾器:主要用來過濾重復請求;
  •  定義不同類型的任務隊列:優先級任務隊列、基于磁盤的任務隊列、基于內存的任務隊列;

請求指紋過濾器又是什么?

在配置文件中,我們可以看到定義的默認指紋過濾器是 RFPDupeFilter: 

  1. class RFPDupeFilter(BaseDupeFilter):  
  2.     """請求指紋過濾器"""  
  3.     def __init__(self, path=Nonedebug=False):  
  4.         self.file = None  
  5.         # 指紋集合 使用的是Set 基于內存  
  6.         self.fingerprints = set()  
  7.         self.logdupes = True  
  8.         self.debug = debug  
  9.         self.logger = logging.getLogger(__name__)  
  10.         # 請求指紋可存入磁盤  
  11.         if path:  
  12.             self.file = open(os.path.join(path, 'requests.seen'), 'a+') 
  13.              self.file.seek(0)  
  14.             self.fingerprints.update(x.rstrip() for x in self.file) 
  15.     @classmethod  
  16.     def from_settings(cls, settings):  
  17.         debug = settings.getbool('DUPEFILTER_DEBUG')  
  18.         return cls(job_dir(settings), debug) 

請求指紋過濾器初始化時,定義了指紋集合,這個集合使用內存實現的 Set,而且可以控制這些指紋是否存入磁盤以供下次重復使用。

也就是說,指紋過濾器的主要職責是:過濾重復請求,可自定義過濾規則。

在下篇文章中我們會介紹到,每個請求是根據什么規則生成指紋的,然后是又如何實現重復請求過濾邏輯的,這里我們先知道它的功能即可。

下面來看調度器定義的任務隊列都有什么作用?

調度器默認定義了 2 種隊列類型:

  •  基于磁盤的任務隊列:在配置文件可配置存儲路徑,每次執行后會把隊列任務保存到磁盤上;
  •  基于內存的任務隊列:每次都在內存中執行,下次啟動則消失;

配置文件默認定義如下: 

  1. # 基于磁盤的任務隊列(后進先出)  
  2. SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleLifoDiskQueue'  
  3. # 基于內存的任務隊列(后進先出)  
  4. SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.LifoMemoryQueue'  
  5. # 優先級隊列  
  6. SCHEDULER_PRIORITY_QUEUE = 'queuelib.PriorityQueue' 

如果我們在配置文件中定義了 JOBDIR 配置項,那么每次執行爬蟲時,都會把任務隊列保存在磁盤中,下次啟動爬蟲時可以重新加載繼續執行我們的任務。

如果沒有定義這個配置項,那么默認使用的是內存隊列。

細心的你可能會發現,默認定義的這些隊列結構都是后進先出的,什么意思呢?

也就是在運行我們的爬蟲代碼時,如果生成一個抓取任務,放入到任務隊列中,那么下次抓取就會從任務隊列中先獲取到這個任務,優先執行。

這么實現意味什么呢?其實意味著:Scrapy 默認的采集規則是深度優先!

如何改變這種機制,變為廣度優先采集呢?這時候我們就要看一下 scrapy.squeues 模塊了,在這里定義了很多種隊列: 

  1. # 先進先出磁盤隊列(pickle序列化)  
  2. PickleFifoDiskQueue = _serializable_queue(queue.FifoDiskQueue, \  
  3.     _pickle_serialize, pickle.loads)  
  4. # 后進先出磁盤隊列(pickle序列化)  
  5. PickleLifoDiskQueue = _serializable_queue(queue.LifoDiskQueue, \  
  6.     _pickle_serialize, pickle.loads)  
  7. # 先進先出磁盤隊列(marshal序列化)  
  8. MarshalFifoDiskQueue = _serializable_queue(queue.FifoDiskQueue, \  
  9.     marshal.dumps, marshal.loads)  
  10. # 后進先出磁盤隊列(marshal序列化)  
  11. MarshalLifoDiskQueue = _serializable_queue(queue.LifoDiskQueue, \  
  12.     marshal.dumps, marshal.loads)  
  13. # 先進先出內存隊列  
  14. FifoMemoryQueue = queue.FifoMemoryQueue  
  15. # 后進先出內存隊列  
  16. LifoMemoryQueue = queue.LifoMemoryQueue 

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

如果你想探究這些隊列是如何實現的,可以參考 Scrapy 作者寫的 scrapy/queuelib 項目,在 Github 上就可以找到,在這里有這些隊列的具體實現。

下載器

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

在默認的配置文件 default_settings.py 中,下載器配置如下:

  1. DOWNLOADER = 'scrapy.core.downloader.Downloader' 

我們來看 Downloader 類的初始化: 

  1. class Downloader(object):  
  2.     """下載器"""  
  3.     def __init__(self, crawler):  
  4.         # 同樣的 拿到settings對象  
  5.         self.settings = crawler.settings  
  6.         self.signals = crawler.signals  
  7.         self.slots = {}  
  8.         self.active = set()  
  9.         # 初始化DownloadHandlers  
  10.         self.handlers = DownloadHandlers(crawler)  
  11.         # 從配置中獲取設置的并發數  
  12.         selfself.total_concurrency = self.settings.getint('CONCURRENT_REQUESTS')  
  13.         # 同一域名并發數  
  14.         selfself.domain_concurrency = self.settings.getint('CONCURRENT_REQUESTS_PER_DOMAIN')  
  15.         # 同一IP并發數  
  16.         selfself.ip_concurrency = self.settings.getint('CONCURRENT_REQUESTS_PER_IP')  
  17.         # 隨機延遲下載時間  
  18.         selfself.randomize_delay = self.settings.getbool('RANDOMIZE_DOWNLOAD_DELAY')  
  19.         # 初始化下載器中間件  
  20.         self.middleware = DownloaderMiddlewareManager.from_crawler(crawler)  
  21.         self._slot_gc_loop = task.LoopingCall(self._slot_gc) 
  22.         self._slot_gc_loop.start(60) 

在這個過程中,主要是初始化了下載處理器、下載器中間件管理器以及從配置文件中拿到抓取請求控制的相關參數。

那么下載處理器是做什么的?下載器中間件又負責哪些工作?

先來看 DownloadHandlers: 

  1. class DownloadHandlers(object):  
  2.     """下載器處理器"""  
  3.     def __init__(self, crawler):  
  4.         self._crawler = crawler  
  5.         self._schemes = {} # 存儲scheme對應的類路徑 后面用于實例化  
  6.         self._handlers = {} # 存儲scheme對應的下載器  
  7.         self._notconfigured = {}  
  8.         # 從配置中找到DOWNLOAD_HANDLERS_BASE 構造下載處理器  
  9.         # 注意:這里是調用getwithbase方法  取的是配置中的XXXX_BASE配置  
  10.         handlers = without_none_values
  11.              crawler.settings.getwithbase('DOWNLOAD_HANDLERS'))  
  12.         # 存儲scheme對應的類路徑 后面用于實例化  
  13.         for scheme, clspath in six.iteritems(handlers):  
  14.             self._schemes[scheme] = clspath  
  15.         crawler.signals.connect(self._close, signals.engine_stopped) 

下載處理器在默認的配置文件中是這樣配置的: 

  1. # 用戶可自定義的下載處理器  
  2. DOWNLOAD_HANDLERS = {}  
  3. # 默認的下載處理器  
  4. DOWNLOAD_HANDLERS_BASE = {  
  5.     'file': 'scrapy.core.downloader.handlers.file.FileDownloadHandler',  
  6.     'http': 'scrapy.core.downloader.handlers.http.HTTPDownloadHandler',  
  7.     'https': 'scrapy.core.downloader.handlers.http.HTTPDownloadHandler',  
  8.     's3': 'scrapy.core.downloader.handlers.s3.S3DownloadHandler',  
  9.     'ftp': 'scrapy.core.downloader.handlers.ftp.FTPDownloadHandler',  

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

但是請注意,在這里,這些下載器是沒有被實例化的,只有在真正發起網絡請求時,才會進行初始化,而且只會初始化一次,后面文章會講到。

下面我們來看下載器中間件 DownloaderMiddlewareManager 初始化過程,同樣地,這里又調用了類方法 from_crawler 進行初始化,而且 DownloaderMiddlewareManager 繼承了MiddlewareManager 類,來看它在初始化做了哪些工作: 

  1. class MiddlewareManager(object):  
  2.     """所有中間件的父類,提供中間件公共的方法"""  
  3.     component_name = 'foo middleware'  
  4.     @classmethod  
  5.     def from_crawler(cls, crawler):  
  6.         # 調用from_settings  
  7.         return cls.from_settings(crawler.settings, crawler)  
  8.      @classmethod  
  9.     def from_settings(cls, settings, crawler=None):  
  10.         # 調用子類_get_mwlist_from_settings得到所有中間件類的模塊  
  11.         mwlist = cls._get_mwlist_from_settings(settings) 
  12.          middlewares = []  
  13.         enabled = []  
  14.         # 依次實例化  
  15.         for clspath in mwlist:  
  16.             try:  
  17.                 # 加載這些中間件模塊  
  18.                 mwcls = load_object(clspath)  
  19.                 # 如果此中間件類定義了from_crawler 則調用此方法實例化  
  20.                 if crawler and hasattr(mwcls, 'from_crawler'):  
  21.                     mw = mwcls.from_crawler(crawler)  
  22.                 # 如果此中間件類定義了from_settings 則調用此方法實例化  
  23.                 elif hasattr(mwcls, 'from_settings'): 
  24.                      mw = mwcls.from_settings(settings)  
  25.                 # 上面2個方法都沒有,則直接調用構造實例化  
  26.                 else:  
  27.                     mw = mwcls()  
  28.                 middlewares.append(mw)  
  29.                 enabled.append(clspath)  
  30.             except NotConfigured as e:  
  31.                 if e.args:  
  32.                     clsname = clspath.split('.')[-1]  
  33.                     logger.warning("Disabled %(clsname)s: %(eargs)s",  
  34.                                    {'clsname': clsname, 'eargs': e.args[0]},  
  35.                                    extra={'crawler': crawler})   
  36.         logger.info("Enabled %(componentname)ss:\n%(enabledlist)s",  
  37.                     {'componentname': cls.component_name,  
  38.                      'enabledlist': pprint.pformat(enabled)},  
  39.                     extra={'crawler': crawler})  
  40.         # 調用構造方法  
  41.         return cls(*middlewares) 
  42.     @classmethod  
  43.     def _get_mwlist_from_settings(cls, settings):  
  44.         # 具體有哪些中間件類,子類定義  
  45.         raise NotImplementedError 
  46.      def __init__(self, *middlewares):  
  47.         self.middlewares = middlewares  
  48.         # 定義中間件方法  
  49.         self.methods = defaultdict(list)  
  50.         for mw in middlewares:  
  51.             self._add_middleware(mw)      
  52.   def _add_middleware(self, mw):  
  53.         # 默認定義的 子類可覆蓋  
  54.         # 如果中間件類有定義open_spider 則加入到methods  
  55.         if hasattr(mw, 'open_spider'):  
  56.             self.methods['open_spider'].append(mw.open_spider)  
  57.         # 如果中間件類有定義close_spider 則加入到methods  
  58.         # methods就是一串中間件的方法鏈 后期會依次調用  
  59.         if hasattr(mw, 'close_spider'):  
  60.             self.methods['close_spider'].insert(0, mw.close_spider) 

DownloaderMiddlewareManager 實例化過程: 

  1. class DownloaderMiddlewareManager(MiddlewareManager):  
  2.  """下載中間件管理器"""  
  3.     component_name = 'downloader middleware'  
  4.     @classmethod  
  5.     def _get_mwlist_from_settings(cls, settings):  
  6.         # 從配置文件DOWNLOADER_MIDDLEWARES_BASE和DOWNLOADER_MIDDLEWARES獲得所有下載器中間件  
  7.         return build_component_list(  
  8.             settings.getwithbase('DOWNLOADER_MIDDLEWARES'))  
  9.     def _add_middleware(self, mw):  
  10.         # 定義下載器中間件請求、響應、異常一串方法  
  11.         if hasattr(mw, 'process_request'):  
  12.             self.methods['process_request'].append(mw.process_request)  
  13.         if hasattr(mw, 'process_response'):  
  14.             self.methods['process_response'].insert(0, mw.process_response)  
  15.         if hasattr(mw, 'process_exception'):  
  16.             self.methods['process_exception'].insert(0, mw.process_exception) 

下載器中間件管理器繼承了 MiddlewareManager 類,然后重寫了 _add_middleware 方法,為下載行為定義默認的下載前、下載后、異常時對應的處理方法。

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

從這里能大概看出,從某個組件流向另一個組件時,會經過一系列中間件,每個中間件都定義了自己的處理流程,相當于一個個管道,輸入時可以針對數據進行處理,然后送達到另一個組件,另一個組件處理完邏輯后,又經過這一系列中間件,這些中間件可再針對這個響應結果進行處理,最終輸出。

Scraper

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

我們來看一下它的初始化過程: 

  1. class Scraper(object):  
  2.     def __init__(self, crawler):  
  3.         self.slot = None  
  4.         # 實例化爬蟲中間件管理器  
  5.         self.spidermw = SpiderMiddlewareManager.from_crawler(crawler)  
  6.         # 從配置文件中加載Pipeline處理器類  
  7.         itemproc_cls = load_object(crawler.settings['ITEM_PROCESSOR'])  
  8.         # 實例化Pipeline處理器  
  9.         self.itemproc = itemproc_cls.from_crawler(crawler)  
  10.         # 從配置文件中獲取同時處理輸出的任務個數  
  11.         self.concurrent_items = crawler.settings.getint('CONCURRENT_ITEMS')  
  12.         self.crawler = crawler  
  13.         self.signals = crawler.signals  
  14.         self.logformatter = crawler.logformatter 

Scraper 創建了 SpiderMiddlewareManager,它的初始化過程: 

  1. class SpiderMiddlewareManager(MiddlewareManager):  
  2.  """爬蟲中間件管理器"""  
  3.     component_name = 'spider middleware'  
  4.     @classmethod  
  5.     def _get_mwlist_from_settings(cls, settings):  
  6.         # 從配置文件中SPIDER_MIDDLEWARES_BASE和SPIDER_MIDDLEWARES獲取默認的爬蟲中間件類  
  7.         return build_component_list(settings.getwithbase('SPIDER_MIDDLEWARES')) 
  8.     def _add_middleware(self, mw):  
  9.         super(SpiderMiddlewareManager, self)._add_middleware(mw)  
  10.         # 定義爬蟲中間件處理方法  
  11.         if hasattr(mw, 'process_spider_input'):  
  12.             self.methods['process_spider_input'].append(mw.process_spider_input)  
  13.         if hasattr(mw, 'process_spider_output'):  
  14.             self.methods['process_spider_output'].insert(0, mw.process_spider_output)  
  15.         if hasattr(mw, 'process_spider_exception'):  
  16.             self.methods['process_spider_exception'].insert(0, mw.process_spider_exception)  
  17.         if hasattr(mw, 'process_start_requests'):  
  18.             self.methods['process_start_requests'].insert(0, mw.process_start_requests) 

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

  1. SPIDER_MIDDLEWARES_BASE = {  
  2.  # 默認的爬蟲中間件類  
  3.     'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,  
  4.     'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,  
  5.     'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,  
  6.     'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,  
  7.     'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,  

這里解釋一下,這些默認的爬蟲中間件的職責:

  •  HttpErrorMiddleware:針對非 200 響應錯誤進行邏輯處理;
  •  OffsiteMiddleware:如果Spider中定義了 allowed_domains,會自動過濾除此之外的域名請求;
  •  RefererMiddleware:追加 Referer 頭信息;
  •  UrlLengthMiddleware:過濾 URL 長度超過限制的請求;
  •  DepthMiddleware:過濾超過指定深度的抓取請求;

當然,在這里你也可以定義自己的爬蟲中間件,來處理自己所需的邏輯。

爬蟲中間件管理器初始化完之后,然后就是 Pipeline 組件的初始化,默認的 Pipeline 組件是 ItemPipelineManager: 

  1. class ItemPipelineManager(MiddlewareManager):  
  2.     component_name = 'item pipeline'  
  3.     @classmethod  
  4.     def _get_mwlist_from_settings(cls, settings):  
  5.         # 從配置文件加載ITEM_PIPELINES_BASE和ITEM_PIPELINES類  
  6.         return build_component_list(settings.getwithbase('ITEM_PIPELINES'))  
  7.     def _add_middleware(self, pipe):  
  8.         super(ItemPipelineManager, self)._add_middleware(pipe)  
  9.         # 定義默認的pipeline處理邏輯  
  10.         if hasattr(pipe, 'process_item'):  
  11.             self.methods['process_item'].append(pipe.process_item) 
  12.     def process_item(self, item, spider):  
  13.         # 依次調用所有子類的process_item方法  
  14.         return self._process_chain('process_item', item, spider) 

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

從 Scraper 的初始化過程我們可以看出,它管理著 Spiders 和 Pipeline 相關的數據交互。

總結

好了,這篇文章我們主要剖析了 Scrapy 涉及到的核心的組件,主要包括:引擎、下載器、調度器、爬蟲類、輸出處理器,以及它們各自都是如何初始化的,在初始化過程中,它們又包含了哪些子模塊來輔助完成這些模塊的功能。

這些組件各司其職,相互協調,共同完成爬蟲的抓取任務,而且從代碼中我們也能發現,每個組件類都是定義在配置文件中的,也就是說我們可以實現自己的邏輯,然后替代這些組件,這樣的設計模式也非常值得我們學習。 

 

責任編輯:龐桂玉 來源: Python中文社區 (ID:python-china)
相關推薦

2017-05-15 21:00:15

大數據Scrapy爬蟲框架

2025-04-01 00:54:00

2022-09-30 10:44:47

Netty組件數據

2017-01-15 14:18:35

大數據HadoopScrapy

2016-10-26 08:57:13

HadoopScrapy大數據

2020-12-29 05:34:48

Scrapy網頁源代碼

2021-06-02 15:10:20

PythonScrapy視頻

2012-07-17 09:13:14

Scrapy

2023-08-28 09:14:20

ScrapyPython

2018-08-08 11:40:24

ScrapyRequest網絡爬蟲

2023-08-29 09:31:01

Scrapy網頁爬蟲

2017-11-29 15:21:53

PythonScrapy爬蟲

2021-01-08 09:07:19

Scrapy框架爬蟲

2021-02-02 07:37:39

NextTickvueDOM

2017-09-16 17:45:32

數據采集Scrapy爬蟲

2021-11-09 09:46:09

ScrapyPython爬蟲

2021-11-08 14:38:50

框架Scrapy 爬蟲

2021-04-12 07:36:15

Scrapy爬蟲框架

2020-12-07 11:23:32

Scrapy爬蟲Python

2016-11-25 13:26:50

Flume架構源碼
點贊
收藏

51CTO技術棧公眾號

日韩av在线免费观看不卡| 艹b视频在线观看| 国产ts丝袜人妖系列视频| 2017亚洲天堂1024| 久久精品福利| 最新国产精品久久精品| 日本国产一区二区三区| 99热自拍偷拍| 刘亦菲毛片一区二区三区| 国产精品二区不卡| 欧美三级乱人伦电影| 久久久久网址| 亚洲伊人成人网| 国产成人精品福利| 亚洲一区欧美一区| 成人在线免费观看视视频| 国产黄色三级网站| a'aaa级片在线观看| 国产一二三精品| 色99之美女主播在线视频| 欧美极品欧美精品欧美图片| 人妻妺妺窝人体色www聚色窝 | 日本精品600av| 蜜桃av一区二区在线观看 | 日本最黄一级片免费在线| 久久精品一本| 亚洲欧美国产精品专区久久| av免费观看网| 羞羞的视频在线观看| 国产日韩欧美电影| 国产精品视频xxxx| 超碰97av在线| 九七电影院97理论片久久tvb| 欧美激情一区二区| 国产区亚洲区欧美区| 国产成人综合在线视频| 日韩一区二区三区精品| 亚洲综合久久久久| 精品视频一区二区| 无码人妻丰满熟妇区bbbbxxxx | 亚洲成av人乱码色午夜| 中文字幕精品在线播放| 国产99久一区二区三区a片| 欧美一区激情| 精品第一国产综合精品aⅴ| 污视频网址在线观看| 色婷婷在线播放| 中文字幕一区在线观看视频| 97超碰资源| 日本亚洲欧美在线| 亚洲天堂日韩在线| 欧美日韩一级二级三级| 日日碰狠狠躁久久躁婷婷| 成人77777| 国产精品一区二区你懂的| 午夜精品三级视频福利| 香蕉网在线播放| 久久亚洲资源中文字| 91国偷自产一区二区三区成为亚洲经典 | 色老头一区二区三区| 精品成人无码一区二区三区| 亚洲伊人伊成久久人综合网| 亚洲国产aⅴ天堂久久| 欧美日本亚洲| 精品人妻少妇AV无码专区| 亚洲欧美日韩一区在线观看| 日韩视频在线观看免费| 中文字幕人妻一区二区三区| 99re久久| 欧美日韩午夜剧场| 一级全黄肉体裸体全过程| 午夜视频免费在线| 国产在线精品一区二区| 亚洲综合中文字幕在线| 波多野结衣在线电影| 国产精品分类| 日韩网站在线观看| 91香蕉一区二区三区在线观看| 欧美黑白配在线| 欧美肥胖老妇做爰| 亚洲 高清 成人 动漫| 六月婷婷综合| 午夜精品一区二区三区电影天堂| 在线天堂一区av电影| 欧美69xxxxx| 成人福利视频网站| 成人妇女免费播放久久久| 国产欧美熟妇另类久久久| 老司机亚洲精品| 97视频在线观看播放| 精品不卡一区二区| 亚洲美洲欧洲综合国产一区| 欧美不卡视频一区发布| 2019男人天堂| 欧美精品一卡| 久久躁狠狠躁夜夜爽| 日本综合在线观看| 国产一区亚洲| 国产精品高潮呻吟久久av野狼| 国产污污视频在线观看| 日日夜夜精品视频免费| 91亚洲国产精品| 日韩精品视频无播放器在线看| 东方aⅴ免费观看久久av| 国产一区二区视频在线观看| 亚洲狼人综合网| 精品一区二区在线看| 国产精品美女久久| 午夜美女福利视频| 国产欧美一区二区精品忘忧草 | 欧美网站一区二区| 91丨porny丨探花| 黄页网站在线| 亚洲国产裸拍裸体视频在线观看乱了 | 免费的av网站| 亚洲成人日韩| 久久精品国产久精国产思思| 成人一级片免费看| 日韩片欧美片| www.亚洲一区| av黄色在线播放| 久久综合狠狠| 福利视频一区二区三区| 成人免费公开视频| 国产精品理论在线观看| 国产一区一区三区| 桃花岛tv亚洲品质| 欧洲在线/亚洲| 欧美美女一级片| 亚洲成人一品| 韩国精品美女www爽爽爽视频| 国产精品人人妻人人爽| 国产传媒欧美日韩成人| 国产麻豆日韩| 黄视频在线播放| 中文字幕中文在线不卡住| 精品中文字幕av| 成人免费一区| 亚洲区中文字幕| 女性裸体视频网站| 黄色一区二区三区四区| 91在线视频精品| 日本三级在线视频| 欧美日韩在线亚洲一区蜜芽| 99久久精品免费视频| 日韩久久电影| 国产成人午夜视频网址| 国产精品玖玖玖| 欧美韩国日本一区| 日本久久精品一区二区| 亚洲一区二区三区久久久| 色诱女教师一区二区三区| 中文字幕免费高清网站| 26uuu欧美| 欧美性受黑人性爽| av在线精品| 不卡av电影在线观看| 国产欧美一级片| 一级精品视频在线观看宜春院| 日本少妇一区二区三区| 香蕉久久99| 久久国产精品99国产精| 国产人妻精品一区二区三区| 中文乱码免费一区二区| av在线观看地址| 日本欧美日韩| 国产一区二区激情| 久久久久免费看| 麻豆视频一区二区| 久久久久久精| 成人软件在线观看| 久久精品国产精品亚洲| 亚洲av无码国产综合专区| 亚洲电影第三页| 女~淫辱の触手3d动漫| 日本不卡免费在线视频| 99热这里只有精品7| 伊色综合久久之综合久久| 国产一区二区三区在线看| 国产精品日日夜夜| 久久精品国产亚洲高清剧情介绍| 自拍偷拍视频在线| 亚洲专区**| 国产999精品久久久| 午夜精品一区二区三| 婷婷综合久久一区二区三区| 亚洲最大天堂网| 国产真实久久| 欧美极品色图| 国产精品成人3p一区二区三区 | 亚洲精品免费在线视频| 成人观看网址| 欧美一区二区黄| 一二三四在线观看视频| 国产传媒久久文化传媒| 黑鬼大战白妞高潮喷白浆| 91精品国产福利在线观看麻豆| 国产精品久久国产精品| 国产成人精品一区二区三区免费 | 午夜精品福利在线视频| 94色蜜桃网一区二区三区| 欧美一级爱爱视频| 国产一区二区三区国产精品| 97视频免费在线观看| 久久精品视频免费看| 欧美日韩在线综合| 国产亚洲精品av| 国产精品美女久久久久久2018| 国产日韩视频一区| 亚洲黄色高清| 久久精品国产精品国产精品污| 精品乱码一区二区三区四区| 国产69精品久久久| 人妻精品一区一区三区蜜桃91| 欧美三级视频在线| 国产视频91在线| 91香蕉视频mp4| 亚洲色图欧美自拍| 欧美精品国产一区二区| 日韩黄色影视| 色综合视频一区二区三区44| 色悠悠久久88| 黄色在线小视频| 亚洲精品xxxx| 亚洲av无码不卡| 天天亚洲美女在线视频| 2021亚洲天堂| 99re免费视频精品全部| 亚洲天堂网站在线| 精品一区二区在线免费观看| wwwwxxxx日韩| 久久婷婷激情| 日本黄色三级大片| 国产一区二区三区久久| 秋霞在线观看一区二区三区| 成人一级视频| 全球成人中文在线| 乡村艳史在线观看| 亚洲一二三在线| 日韩电影免费| 精品偷拍各种wc美女嘘嘘| 亚洲国产无线乱码在线观看| 中文字幕亚洲一区二区va在线| 国产熟女一区二区| 国产欧美日韩在线| 免费福利视频网站| 久久久久国产成人精品亚洲午夜| 亚洲天堂国产视频| 麻豆精品在线播放| 天天干天天草天天| 麻豆久久久久久| 青娱乐精品在线| 99re国产精品| 男人揉女人奶房视频60分| 亚洲欧美久久久| 成人中文字幕av| 欧美a级在线| 日韩久久久久久久久久久久| 国产videos久久| 高清日韩一区| 红杏aⅴ成人免费视频| 国产一区二区三区四区五区在线| 成人精品一区二区三区电影| 国产日韩精品综合网站| 伊人亚洲精品| 99久久国产免费免费| 久久久成人av毛片免费观看| 欧美激情久久久久| 成年人免费在线视频| 中文字幕日韩av| 午夜视频在线免费播放| 亚洲欧洲国产伦综合| 二区三区在线播放| 亚洲韩国日本中文字幕| 午夜影院在线视频| 亚洲性猛交xxxxwww| 日本在线免费看| 久久久这里只有精品视频| 亚洲黄色免费看| 国产日韩欧美影视| 91蜜桃臀久久一区二区| 国产日韩欧美黄色| 视频一区日韩精品| 精品午夜一区二区| 日韩黄色大片| 和岳每晚弄的高潮嗷嗷叫视频| 天天做综合网| 给我免费播放片在线观看| 久久中文在线| 乱码一区二区三区| 国产一区二区三区在线观看免费 | 97人人精品| 欧美日韩精品免费观看| 91综合久久| 一区二区传媒有限公司| 久久精品二区亚洲w码| 美女搡bbb又爽又猛又黄www| 国产日韩精品久久久| 欧美激情一区二区视频| 亚洲品质自拍视频| 二区三区四区视频| 午夜久久福利影院| 国产精品一区二区av白丝下载| 日韩h在线观看| 天天影院图片亚洲| 亚洲国语精品自产拍在线观看| 瑟瑟视频在线| 欧美一级黄色网| 欧美电影免费观看| 91成人免费视频| 成人久久综合| 亚洲日本精品一区| 99精品综合| 国产福利一区视频| 成人午夜伦理影院| 91高清免费观看| 尤物视频一区二区| 国产亚洲精品久久777777| 欧美色网一区二区| 欧美精品a∨在线观看不卡| 欧美日产国产成人免费图片| 欧美videossex| 97在线精品视频| 秋霞一区二区| 日本特级黄色大片| 免费成人你懂的| 日韩精品电影一区二区| 中文字幕国产一区| 天天操天天操天天操天天| 欧美va亚洲va在线观看蝴蝶网| 欧美天堂在线视频| 欧美成人精品h版在线观看| 福利一区二区免费视频| 欧美少妇一区| 久久成人亚洲| 蜜桃传媒一区二区亚洲av| 精品成人乱色一区二区| 日本高清视频免费观看| 欧美精品videos| 国模套图日韩精品一区二区| 国产精品视频免费一区| 欧美日韩亚洲一区二区三区在线| 中文字幕 欧美日韩| 成人综合在线观看| 欧美黑人一级片| 日韩女优电影在线观看| 三区在线视频| 2020欧美日韩在线视频| 欧美绝顶高潮抽搐喷水合集| 日本a视频在线观看| 成人美女视频在线看| 国产无套粉嫩白浆内谢| 亚洲缚视频在线观看| av在线最新| 久久久亚洲综合网站| 久久蜜桃精品| 日本理论中文字幕| 欧美三级日韩三级| 精品国产99久久久久久| 97操在线视频| 亚洲黄色影院| 国产ts在线播放| 欧美色大人视频| h视频在线免费观看| 国产成人av在线播放| 精品久久久久久久| av网站手机在线观看| 99精品视频一区| 精品国产乱子伦| 久久久国产精品x99av | 久久亚洲精选| 黄色录像二级片| 欧美精品一区二区三区高清aⅴ | 91gao视频| 中文日韩在线| 国产7777777| 日韩欧美一区二区久久婷婷| 九色porny自拍视频在线播放| 国产欧美一区二区三区在线看| 婷婷亚洲图片| 加勒比精品视频| 欧美日韩中字一区| 欧美性video| 日本免费高清一区二区| 激情综合五月婷婷| 日日操免费视频| 欧美mv日韩mv国产网站app| 不卡一二三区| 影音先锋成人资源网站| 毛片不卡一区二区| 久久免费在线观看视频| 亚洲人在线观看| 日韩精品免费视频一区二区三区 | 欧美精品一区二区三区很污很色的| 久久爱91午夜羞羞| www.欧美黄色| 中文字幕在线观看一区二区| 天堂av一区二区三区| 欧美精品福利在线| 久久99国产成人小视频|