日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

cmd怎么运行http_Scrapy源码剖析(二)Scrapy是如何运行起来的?

發(fā)布時(shí)間:2025/4/5 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cmd怎么运行http_Scrapy源码剖析(二)Scrapy是如何运行起来的? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
閱讀本文大約需要 15 分鐘。本文章代碼較多,如果手機(jī)端閱讀體驗(yàn)不好,建議先收藏后在 PC 端閱讀。在上篇文章:Scrapy源碼剖析(一)架構(gòu)概覽,我們主要從整體上了解了 Scrapy 的架構(gòu)和數(shù)據(jù)流轉(zhuǎn),并沒(méi)有深入分析每個(gè)模塊。從這篇文章開(kāi)始,我將帶你詳細(xì)剖析 Scrapy 的運(yùn)行原理。這篇文章,我們先從最基礎(chǔ)的運(yùn)行入口來(lái)講,來(lái)看一下 Scrapy 究竟是如何運(yùn)行起來(lái)的。

scrapy 命令從哪來(lái)?

當(dāng)我們基于 Scrapy 寫(xiě)好一個(gè)爬蟲(chóng)后,想要把我們的爬蟲(chóng)運(yùn)行起來(lái),怎么做?非常簡(jiǎn)單,只需要執(zhí)行以下命令就可以了。?scrapy?crawl?通過(guò)這個(gè)命令,我們的爬蟲(chóng)就真正開(kāi)始工作了。那么從命令行到執(zhí)行爬蟲(chóng)邏輯,這個(gè)過(guò)程中到底發(fā)生了什么?在開(kāi)始之前,不知道你有沒(méi)有和我一樣的疑惑,我們執(zhí)行的 scrapy命令從何而來(lái)?實(shí)際上,當(dāng)你成功安裝好 Scrapy 后,使用如下命令,就能找到這個(gè)命令文件,這個(gè)文件就是 Scrapy 的運(yùn)行入口:$?which?scrapy
/usr/local/bin/scrapy使用編輯打開(kāi)這個(gè)文件,你會(huì)發(fā)現(xiàn),它其實(shí)它就是一個(gè) Python 腳本,而且代碼非常少。import?reimport?sysfrom?scrapy.cmdline?import?executeif?__name__?==?'__main__':
????sys.argv[0]?=?re.sub(r'(-script\.pyw|\.exe)?$',?'',?sys.argv[0])
????sys.exit(execute())安裝好 Scrapy 后,為什么入口點(diǎn)是這里呢?答案就在于 Scrapy 的安裝文件 setup.py中,我們找到這個(gè)文件,就會(huì)發(fā)現(xiàn)在這個(gè)文件里,已經(jīng)聲明好了程序的運(yùn)行入口處:from?os.path?import?dirname,?joinfrom?setuptools?import?setup,?find_packages
setup(
????name='Scrapy',
????version=version,
????url='http://scrapy.org',
????...
????entry_points={??????#?運(yùn)行入口在這里:scrapy.cmdline:execute 'console_scripts':?['scrapy?=?scrapy.cmdline:execute']
????},
????classifiers=[
????????...
????],
????install_requires=[
????????...
????],
)我們需要關(guān)注的是 entry_points配置,它就是調(diào)用 Scrapy 開(kāi)始的地方,也就是cmdline.py的 execute方法。也就是說(shuō),我們?cè)诎惭b Scrapy 的過(guò)程中,setuptools這個(gè)包管理工具,就會(huì)把上述代碼生成好并放在可執(zhí)行路徑下,這樣當(dāng)我們調(diào)用 scrapy命令時(shí),就會(huì)調(diào)用 Scrapy 模塊下的 cmdline.py的 execute方法。而且在這這里,我們可以學(xué)到一個(gè)小技巧——如何用 Python 編寫(xiě)一個(gè)可執(zhí)行文件?其實(shí)非常簡(jiǎn)單,模仿上面的思路,只需要以下幾步即可完成:
  • 編寫(xiě)一個(gè)帶有 main 方法的 Python 模塊(首行必須注明 Python 執(zhí)行路徑)
  • 去掉.py后綴名
  • 修改權(quán)限為可執(zhí)行(chmod +x 文件名)
  • 直接用文件名就可以執(zhí)行這個(gè) Python 文件
  • 例如,我們創(chuàng)建一個(gè)文件 mycmd,在這個(gè)文件中編寫(xiě)一個(gè) main方法,這個(gè)方法編寫(xiě)我們想要的執(zhí)行的邏輯,之后執(zhí)行 chmod +x mycmd把這個(gè)文件權(quán)限變成可執(zhí)行,最后通過(guò) ./mycmd就可以執(zhí)行這段代碼了,而不再需要通過(guò) python 方式就可以執(zhí)行了,是不是很簡(jiǎn)單?

    運(yùn)行入口(execute.py)

    現(xiàn)在,我們已經(jīng)知道了 Scrapy 的運(yùn)行入口是 scrapy/cmdline.py的 execute方法,那我們就看一下這個(gè)方法。def?execute(argv=None,?settings=None):if?argv?is?None:
    ????????argv?=?sys.argv#?---?兼容低版本scrapy.conf.settings的配置?---if?settings?is?None?and?'scrapy.conf'?in?sys.modules:from?scrapy?import?confif?hasattr(conf,?'settings'):
    ????????????settings?=?conf.settings#?-----------------------------------------#?初始化環(huán)境、獲取項(xiàng)目配置參數(shù)?返回settings對(duì)象if?settings?is?None:
    ????????settings?=?get_project_settings()#?校驗(yàn)棄用的配置項(xiàng)
    ????check_deprecated_settings(settings)#?---?兼容低版本scrapy.conf.settings的配置?---import?warningsfrom?scrapy.exceptions?import?ScrapyDeprecationWarningwith?warnings.catch_warnings():
    ????????warnings.simplefilter("ignore",?ScrapyDeprecationWarning)from?scrapy?import?conf
    ????????conf.settings?=?settings#?---------------------------------------#?執(zhí)行環(huán)境是否在項(xiàng)目中?主要檢查scrapy.cfg配置文件是否存在
    ????inproject?=?inside_project()#?讀取commands文件夾?把所有的命令類(lèi)轉(zhuǎn)換為{cmd_name:?cmd_instance}的字典
    ????cmds?=?_get_commands_dict(settings,?inproject)#?從命令行解析出執(zhí)行的是哪個(gè)命令
    ????cmdname?=?_pop_command_name(argv)
    ????parser?=?optparse.OptionParser(formatter=optparse.TitledHelpFormatter(),?\
    ????????conflict_handler='resolve')if?not?cmdname:
    ????????_print_commands(settings,?inproject)
    ????????sys.exit(0)elif?cmdname?not?in?cmds:
    ????????_print_unknown_command(settings,?cmdname,?inproject)
    ????????sys.exit(2)#?根據(jù)命令名稱(chēng)找到對(duì)應(yīng)的命令實(shí)例
    ????cmd?=?cmds[cmdname]
    ????parser.usage?=?"scrapy?%s?%s"?%?(cmdname,?cmd.syntax())
    ????parser.description?=?cmd.long_desc()#?設(shè)置項(xiàng)目配置和級(jí)別為command
    ????settings.setdict(cmd.default_settings,?priority='command')
    ????cmd.settings?=?settings#?添加解析規(guī)則
    ????cmd.add_options(parser)#?解析命令參數(shù),并交由Scrapy命令實(shí)例處理
    ????opts,?args?=?parser.parse_args(args=argv[1:])
    ????_run_print_help(parser,?cmd.process_options,?args,?opts)#?初始化CrawlerProcess實(shí)例?并給命令實(shí)例添加crawler_process屬性
    ????cmd.crawler_process?=?CrawlerProcess(settings)#?執(zhí)行命令實(shí)例的run方法
    ????_run_print_help(parser,?_run_command,?cmd,?args,?opts)
    ????sys.exit(cmd.exitcode)這塊代碼就是 Scrapy 執(zhí)行的運(yùn)行入口了,我們根據(jù)注釋就能看到,這里的主要工作包括配置初始化、命令解析、爬蟲(chóng)類(lèi)加載、運(yùn)行爬蟲(chóng)這幾步。了解了整個(gè)入口的流程,下面我會(huì)對(duì)每個(gè)步驟進(jìn)行詳細(xì)的分析。

    初始化項(xiàng)目配置

    首先第一步,根據(jù)環(huán)境初始化配置,在這里有一些兼容低版本 Scrapy 配置的代碼,我們忽略就好。我們重點(diǎn)來(lái)看配置是如何初始化的。這主要和環(huán)境變量和 scrapy.cfg有關(guān),通過(guò)調(diào)用 ?get_project_settings方法,最終生成一個(gè) Settings實(shí)例。def?get_project_settings():#?環(huán)境變量中是否有SCRAPY_SETTINGS_MODULE配置if?ENVVAR?not?in?os.environ:
    ????????project?=?os.environ.get('SCRAPY_PROJECT',?'default')#?初始化環(huán)境?找到用戶(hù)配置文件settings.py?設(shè)置到環(huán)境變量SCRAPY_SETTINGS_MODULE中
    ????????init_env(project)#?加載默認(rèn)配置文件default_settings.py?生成settings實(shí)例
    ????settings?=?Settings()#?取得用戶(hù)配置文件
    ????settings_module_path?=?os.environ.get(ENVVAR)#?如果有用戶(hù)配置?則覆蓋默認(rèn)配置if?settings_module_path:
    ????????settings.setmodule(settings_module_path,?priority='project')#?如果環(huán)境變量中有其他scrapy相關(guān)配置也覆蓋
    ????pickled_settings?=?os.environ.get("SCRAPY_PICKLED_SETTINGS_TO_OVERRIDE")if?pickled_settings:
    ????????settings.setdict(pickle.loads(pickled_settings),?priority='project')
    ????env_overrides?=?{k[7:]:?v?for?k,?v?in?os.environ.items()?if
    ?????????????????????k.startswith('SCRAPY_')}if?env_overrides:
    ????????settings.setdict(env_overrides,?priority='project')return?settings在初始配置時(shí),會(huì)加載默認(rèn)的配置文件 default_settings.py,主要邏輯在 Settings類(lèi)中。class?Settings(BaseSettings):def?__init__(self,?values=None,?priority='project'):#?調(diào)用父類(lèi)構(gòu)造初始化
    ????????super(Settings,?self).__init__()#?把default_settings.py的所有配置set到settings實(shí)例中
    ????????self.setmodule(default_settings,?'default')#?把a(bǔ)ttributes屬性也set到settings實(shí)例中for?name,?val?in?six.iteritems(self):if?isinstance(val,?dict):
    ????????????????self.set(name,?BaseSettings(val,?'default'),?'default')
    ????????self.update(values,?priority)可以看到,首先把默認(rèn)配置文件 default_settings.py中的所有配置項(xiàng)設(shè)置到 Settings中,而且這個(gè)配置是有優(yōu)先級(jí)的。這個(gè)默認(rèn)配置文件 default_settings.py是非常重要的,我們讀源碼時(shí)有必要重點(diǎn)關(guān)注一下里面的內(nèi)容,這里包含了所有組件的默認(rèn)配置,以及每個(gè)組件的類(lèi)模塊,例如調(diào)度器類(lèi)、爬蟲(chóng)中間件類(lèi)、下載器中間件類(lèi)、下載處理器類(lèi)等等。#?下載器類(lèi)
    DOWNLOADER?=?'scrapy.core.downloader.Downloader'#?調(diào)度器類(lèi)
    CHEDULER?=?'scrapy.core.scheduler.Scheduler'#?調(diào)度隊(duì)列類(lèi)
    SCHEDULER_DISK_QUEUE?=?'scrapy.squeues.PickleLifoDiskQueue'
    SCHEDULER_MEMORY_QUEUE?=?'scrapy.squeues.LifoMemoryQueue'
    SCHEDULER_PRIORITY_QUEUE?=?'scrapy.pqueues.ScrapyPriorityQueue'有沒(méi)有感覺(jué)比較奇怪,默認(rèn)配置中配置了這么多類(lèi)模塊,這是為什么?這其實(shí)是 Scrapy 特性之一,它這么做的好處是:任何模塊都是可替換的。什么意思呢?例如,你覺(jué)得默認(rèn)的調(diào)度器功能不夠用,那么你就可以按照它定義的接口標(biāo)準(zhǔn),自己實(shí)現(xiàn)一個(gè)調(diào)度器,然后在自己的配置文件中,注冊(cè)自己的調(diào)度器類(lèi),那么 Scrapy 運(yùn)行時(shí)就會(huì)加載你的調(diào)度器執(zhí)行了,這極大地提高了我們的靈活性!所以,只要在默認(rèn)配置文件中配置的模塊類(lèi),都是可替換的。

    檢查運(yùn)行環(huán)境是否在項(xiàng)目中

    初始化完配置之后,下面一步是檢查運(yùn)行環(huán)境是否在爬蟲(chóng)項(xiàng)目中。我們知道,scrapy命令有的是依賴(lài)項(xiàng)目運(yùn)行的,有的命令則是全局的。這里主要通過(guò)就近查找 scrapy.cfg文件來(lái)確定是否在項(xiàng)目環(huán)境中,主要邏輯在 inside_project方法中。def?inside_project():#?檢查此環(huán)境變量是否存在(上面已設(shè)置)
    ????scrapy_module?=?os.environ.get('SCRAPY_SETTINGS_MODULE')if?scrapy_module?is?not?None:try:
    ????????????import_module(scrapy_module)except?ImportError?as?exc:
    ????????????warnings.warn("Cannot?import?scrapy?settings?module?%s:?%s"?%?(scrapy_module,?exc))else:return?True#?如果環(huán)境變量沒(méi)有?就近查找scrapy.cfg?找得到就認(rèn)為是在項(xiàng)目環(huán)境中return?bool(closest_scrapy_cfg())運(yùn)行環(huán)境是否在爬蟲(chóng)項(xiàng)目中的依據(jù)就是能否找到 scrapy.cfg文件,如果能找到,則說(shuō)明是在爬蟲(chóng)項(xiàng)目中,否則就認(rèn)為是執(zhí)行的全局命令。

    組裝命令實(shí)例集合

    再向下看,就到了加載命令的邏輯了。我們知道 scrapy包括很多命令,例如 scrapy crawl、 scrapy fetch等等,那這些命令是從哪來(lái)的?答案就在 _get_commands_dict方法中。def?_get_commands_dict(settings,?inproject):#?導(dǎo)入commands文件夾下的所有模塊?生成{cmd_name:?cmd}的字典集合
    ????cmds?=?_get_commands_from_module('scrapy.commands',?inproject)
    ????cmds.update(_get_commands_from_entry_points(inproject))#?如果用戶(hù)自定義配置文件中有COMMANDS_MODULE配置?則加載自定義的命令類(lèi)
    ????cmds_module?=?settings['COMMANDS_MODULE']if?cmds_module:
    ????????cmds.update(_get_commands_from_module(cmds_module,?inproject))return?cmdsdef?_get_commands_from_module(module,?inproject):
    ????d?=?{}#?找到這個(gè)模塊下所有的命令類(lèi)(ScrapyCommand子類(lèi))for?cmd?in?_iter_command_classes(module):if?inproject?or?not?cmd.requires_project:#?生成{cmd_name:?cmd}字典
    ????????????cmdname?=?cmd.__module__.split('.')[-1]
    ????????????d[cmdname]?=?cmd()return?ddef?_iter_command_classes(module_name):#?迭代這個(gè)包下的所有模塊?找到ScrapyCommand的子類(lèi)for?module?in?walk_modules(module_name):for?obj?in?vars(module).values():if?inspect.isclass(obj)?and?\
    ????????????????????issubclass(obj,?ScrapyCommand)?and?\
    ????????????????????obj.__module__?==?module.__name__:yield?obj這個(gè)過(guò)程主要是,導(dǎo)入 commands文件夾下的所有模塊,最終生成一個(gè) {cmd_name: cmd}字典集合,如果用戶(hù)在配置文件中也配置了自定義的命令類(lèi),也會(huì)追加進(jìn)去。也就是說(shuō),我們自己也可以編寫(xiě)自己的命令類(lèi),然后追加到配置文件中,之后就可以使用自己定義的命令了。

    解析命令

    加載好命令類(lèi)后,就開(kāi)始解析我們具體執(zhí)行的哪個(gè)命令了,解析邏輯比較簡(jiǎn)單:def?_pop_command_name(argv):
    ????i?=?0for?arg?in?argv[1:]:if?not?arg.startswith('-'):del?argv[i]return?arg
    ????????i?+=?1這個(gè)過(guò)程就是解析命令行,例如執(zhí)行 scrapy crawl ,這個(gè)方法會(huì)解析出 crawl,通過(guò)上面生成好的命令類(lèi)的字典集合,就能找到 commands目錄下的 crawl.py文件,最終執(zhí)行的就是它的 Command類(lèi)。

    解析命令行參數(shù)

    找到對(duì)應(yīng)的命令實(shí)例后,調(diào)用 cmd.process_options方法解析我們的參數(shù):def?process_options(self,?args,?opts):#?首先調(diào)用了父類(lèi)的process_options?解析統(tǒng)一固定的參數(shù)
    ????ScrapyCommand.process_options(self,?args,?opts)try:#?命令行參數(shù)轉(zhuǎn)為字典
    ????????opts.spargs?=?arglist_to_dict(opts.spargs)except?ValueError:raise?UsageError("Invalid?-a?value,?use?-a?NAME=VALUE",?print_help=False)if?opts.output:if?opts.output?==?'-':
    ????????????self.settings.set('FEED_URI',?'stdout:',?priority='cmdline')else:
    ????????????self.settings.set('FEED_URI',?opts.output,?priority='cmdline')
    ????????feed_exporters?=?without_none_values(
    ????????????self.settings.getwithbase('FEED_EXPORTERS'))
    ????????valid_output_formats?=?feed_exporters.keys()if?not?opts.output_format:
    ????????????opts.output_format?=?os.path.splitext(opts.output)[1].replace(".",?"")if?opts.output_format?not?in?valid_output_formats:raise?UsageError("Unrecognized?output?format?'%s',?set?one""?using?the?'-t'?switch?or?as?a?file?extension""?from?the?supported?list?%s"?%?(opts.output_format,
    ????????????????????????????????????????????????????????????????tuple(valid_output_formats)))
    ????????self.settings.set('FEED_FORMAT',?opts.output_format,?priority='cmdline')這個(gè)過(guò)程就是解析命令行其余的參數(shù),固定參數(shù)解析交給父類(lèi)處理,例如輸出位置等。其余不同的參數(shù)由不同的命令類(lèi)解析。

    初始化CrawlerProcess

    一切準(zhǔn)備就緒,最后初始化 CrawlerProcess實(shí)例,然后運(yùn)行對(duì)應(yīng)命令實(shí)例的 run方法。cmd.crawler_process?=?CrawlerProcess(settings)
    _run_print_help(parser,?_run_command,?cmd,?args,?opts)我們開(kāi)始運(yùn)行一個(gè)爬蟲(chóng)一般使用的是 scrapy crawl ,也就是說(shuō)最終調(diào)用的是 commands/crawl.py的 run方法:def?run(self,?args,?opts):if?len(args)?1:raise?UsageError()elif?len(args)?>?1:raise?UsageError("running?'scrapy?crawl'?with?more?than?one?spider?is?no?longer?supported")
    ????spname?=?args[0]
    ????self.crawler_process.crawl(spname,?**opts.spargs)
    ????self.crawler_process.start()run方法中調(diào)用了 CrawlerProcess實(shí)例的 crawl和 start方法,就這樣整個(gè)爬蟲(chóng)程序就會(huì)運(yùn)行起來(lái)了。我們先來(lái)看CrawlerProcess初始化:class?CrawlerProcess(CrawlerRunner):def?__init__(self,?settings=None):#?調(diào)用父類(lèi)初始化
    ????????super(CrawlerProcess,?self).__init__(settings)#?信號(hào)和log初始化
    ????????install_shutdown_handlers(self._signal_shutdown)
    ????????configure_logging(self.settings)
    ????????log_scrapy_info(self.settings)其中,構(gòu)造方法中調(diào)用了父類(lèi) CrawlerRunner的構(gòu)造方法:class?CrawlerRunner(object):def?__init__(self,?settings=None):if?isinstance(settings,?dict)?or?settings?is?None:
    ????????????settings?=?Settings(settings)
    ????????self.settings?=?settings#?獲取爬蟲(chóng)加載器
    ????????self.spider_loader?=?_get_spider_loader(settings)
    ????????self._crawlers?=?set()
    ????????self._active?=?set()初始化時(shí),調(diào)用了 _get_spider_loader方法:def?_get_spider_loader(settings):#?讀取配置文件中的SPIDER_MANAGER_CLASS配置項(xiàng)if?settings.get('SPIDER_MANAGER_CLASS'):
    ????????warnings.warn('SPIDER_MANAGER_CLASS?option?is?deprecated.?''Please?use?SPIDER_LOADER_CLASS.',
    ????????????category=ScrapyDeprecationWarning,?stacklevel=2
    ????????)
    ????cls_path?=?settings.get('SPIDER_MANAGER_CLASS',
    ????????????????????????????settings.get('SPIDER_LOADER_CLASS'))
    ????loader_cls?=?load_object(cls_path)try:
    ????????verifyClass(ISpiderLoader,?loader_cls)except?DoesNotImplement:
    ????????warnings.warn('SPIDER_LOADER_CLASS?(previously?named?SPIDER_MANAGER_CLASS)?does?''not?fully?implement?scrapy.interfaces.ISpiderLoader?interface.?''Please?add?all?missing?methods?to?avoid?unexpected?runtime?errors.',
    ????????????category=ScrapyDeprecationWarning,?stacklevel=2
    ????????)return?loader_cls.from_settings(settings.frozencopy())這里會(huì)讀取默認(rèn)配置文件中的 spider_loader項(xiàng),默認(rèn)配置是 spiderloader.SpiderLoader類(lèi),從名字我們也能看出來(lái),這個(gè)類(lèi)是用來(lái)加載我們編寫(xiě)好的爬蟲(chóng)類(lèi)的,下面看一下這個(gè)類(lèi)的具體實(shí)現(xiàn)。@implementer(ISpiderLoader)class?SpiderLoader(object):def?__init__(self,?settings):#?配置文件獲取存放爬蟲(chóng)腳本的路徑
    ????????self.spider_modules?=?settings.getlist('SPIDER_MODULES')
    ????????self._spiders?=?{}#?加載所有爬蟲(chóng)
    ????????self._load_all_spiders()def?_load_spiders(self,?module):#?組裝成{spider_name:?spider_cls}的字典for?spcls?in?iter_spider_classes(module):
    ????????????self._spiders[spcls.name]?=?spclsdef?_load_all_spiders(self):for?name?in?self.spider_modules:for?module?in?walk_modules(name):
    ????????????????self._load_spiders(module)可以看到,在這里爬蟲(chóng)加載器會(huì)加載所有的爬蟲(chóng)腳本,最后生成一個(gè) {spider_name: spider_cls}的字典,所以我們?cè)趫?zhí)行 scarpy crawl 時(shí),Scrapy 就能找到我們的爬蟲(chóng)類(lèi)。

    運(yùn)行爬蟲(chóng)

    CrawlerProcess初始化完之后,調(diào)用它的 crawl方法:def?crawl(self,?crawler_or_spidercls,?*args,?**kwargs):#?創(chuàng)建crawler
    ????crawler?=?self.create_crawler(crawler_or_spidercls)return?self._crawl(crawler,?*args,?**kwargs)def?_crawl(self,?crawler,?*args,?**kwargs):
    ????self.crawlers.add(crawler)#?調(diào)用Crawler的crawl方法
    ????d?=?crawler.crawl(*args,?**kwargs)
    ????self._active.add(d)def?_done(result):
    ????????self.crawlers.discard(crawler)
    ????????self._active.discard(d)return?resultreturn?d.addBoth(_done)def?create_crawler(self,?crawler_or_spidercls):if?isinstance(crawler_or_spidercls,?Crawler):return?crawler_or_spiderclsreturn?self._create_crawler(crawler_or_spidercls)def?_create_crawler(self,?spidercls):#?如果是字符串?則從spider_loader中加載這個(gè)爬蟲(chóng)類(lèi)if?isinstance(spidercls,?six.string_types):
    ????????spidercls?=?self.spider_loader.load(spidercls)#?否則創(chuàng)建Crawlerreturn?Crawler(spidercls,?self.settings)這個(gè)過(guò)程會(huì)創(chuàng)建 Cralwer實(shí)例,然后調(diào)用它的 crawl方法:@defer.inlineCallbacksdef?crawl(self,?*args,?**kwargs):assert?not?self.crawling,?"Crawling?already?taking?place"
    ????self.crawling?=?Truetry:#?到現(xiàn)在?才是實(shí)例化一個(gè)爬蟲(chóng)實(shí)例
    ????????self.spider?=?self._create_spider(*args,?**kwargs)#?創(chuàng)建引擎
    ????????self.engine?=?self._create_engine()#?調(diào)用爬蟲(chóng)類(lèi)的start_requests方法
    ????????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?=?Falseif?self.engine?is?not?None:yield?self.engine.close()if?six.PY2:
    ????????????six.reraise(*exc_info)raisedef?_create_spider(self,?*args,?**kwargs):return?self.spidercls.from_crawler(self,?*args,?**kwargs)到這里,才會(huì)對(duì)我們的爬蟲(chóng)類(lèi)創(chuàng)建一個(gè)實(shí)例對(duì)象,然后創(chuàng)建引擎,之后調(diào)用爬蟲(chóng)類(lèi)的 start_requests方法獲取種子 URL,最后交給引擎執(zhí)行。最后來(lái)看 Cralwer是如何開(kāi)始運(yùn)行的額,也就是它的 start方法:def?start(self,?stop_after_crawl=True):if?stop_after_crawl:
    ????????d?=?self.join()if?d.called:return
    ????????d.addBoth(self._stop_reactor)
    ????reactor.installResolver(self._get_dns_resolver())#?配置reactor的池子大小(可修改REACTOR_THREADPOOL_MAXSIZE調(diào)整)
    ????tp?=?reactor.getThreadPool()
    ????tp.adjustPoolsize(maxthreads=self.settings.getint('REACTOR_THREADPOOL_MAXSIZE'))
    ????reactor.addSystemEventTrigger('before',?'shutdown',?self.stop)#?開(kāi)始執(zhí)行
    ????reactor.run(installSignalHandlers=False)在這里有一個(gè)叫做 reactor的模塊。reactor是個(gè)什么東西呢?它是 Twisted模塊的事件管理器,我們只要把需要執(zhí)行的事件注冊(cè)到 reactor中,然后調(diào)用它的 run方法,它就會(huì)幫我們執(zhí)行注冊(cè)好的事件,如果遇到網(wǎng)絡(luò)IO等待,它會(huì)自動(dòng)幫切換到可執(zhí)行的事件上,非常高效。在這里我們不用深究 reactor是如何工作的,你可以把它想象成一個(gè)線(xiàn)程池,只是采用注冊(cè)回調(diào)的方式來(lái)執(zhí)行事件。到這里,Scrapy 運(yùn)行的入口就分析完了,之后爬蟲(chóng)的調(diào)度邏輯就交由引擎 ExecuteEngine處理了,引擎會(huì)協(xié)調(diào)多個(gè)組件,相互配合完成整個(gè)任務(wù)的執(zhí)行。

    總結(jié)

    總結(jié)一下,Scrapy 在真正運(yùn)行前,需要做的工作包括配置環(huán)境初始化、命令類(lèi)的加載、爬蟲(chóng)模塊的加載,以及命令類(lèi)和參數(shù)解析,之后運(yùn)行我們的爬蟲(chóng)類(lèi),最終,這個(gè)爬蟲(chóng)類(lèi)的調(diào)度交給引擎處理。這里我把整個(gè)流程也總結(jié)成了思維導(dǎo)圖,方便你理解:好了,Scrapy 是如何運(yùn)行的代碼剖析就先分析到這里,下篇文章我們會(huì)深入剖析各個(gè)核心組件,分析它們都是負(fù)責(zé)做什么工作的,以及它們之間又是如何協(xié)調(diào)完成抓取任務(wù)的,敬請(qǐng)期待。近期文章推薦:Scrapy源碼剖析(一)架構(gòu)概覽如何構(gòu)建一個(gè)通用的垂直爬蟲(chóng)平臺(tái)?如何搭建一個(gè)爬蟲(chóng)代理服務(wù)?
    長(zhǎng)按關(guān)注「水滴與銀彈」公眾號(hào),7年資深后端研發(fā),和你分享更多優(yōu)質(zhì)技術(shù)干貨。

    總結(jié)

    以上是生活随笔為你收集整理的cmd怎么运行http_Scrapy源码剖析(二)Scrapy是如何运行起来的?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产精品卡一卡二 | 可以看毛片的网站 | 欧美黄色网 | 欧美z○zo重口另类黄 | 国产精品人妻 | 久草免费在线视频 | 国产粉嫩在线观看 | 18成人免费观看网站下载 | 亚洲欧美小视频 | 亚洲国产婷婷香蕉久久久久久99 | 亚洲精品一区二区三区区别 | 吃瓜网今日吃瓜 热门大瓜 色婷在线 | 九色91蝌蚪 | 中文字幕乱码亚洲精品一区 | 成人性做爰aaa片免费看不忠 | 国产精品一区不卡 | 国产精品99在线观看 | www.一区二区三区四区 | 亚洲综合激情小说 | 国内偷拍一区二区 | 无码一区二区波多野结衣播放搜索 | 日日射影院 | 国产精品伦一区 | 日本一区精品视频 | 久久久久久日产精品 | 日韩精品偷拍 | 五月天丁香久久 | 色婷婷av久久久久久久 | 给我看高清的视频在线观看 | 玉足女爽爽91 | 亚洲免费中文 | 国产日本在线播放 | 日本aⅴ在线 | 欧美日韩在线视频观看 | caoporen在线| 日本亚洲最大的色成网站www | 欧美一级片免费在线观看 | 就操在线 | 亲切的金子餐桌片段的金子 | 国产精品男同 | 国产精品久久久久久 | www.日韩欧美 | 日本在线不卡一区二区三区 | 少妇丰满尤物大尺度写真 | 黄瓜视频在线观看 | 日韩欧美高清一区 | 婷婷丁香六月天 | 91手机在线观看 | 中文字幕一区二区三区久久久 | 国产精品无码午夜福利 | 91av在线免费| 一区二区三区在线观看 | 2018天天操| 亚洲天堂麻豆 | 久久精品视频免费看 | 久久久免费在线观看 | 人人爽人人草 | 四虎网站在线观看 | 亚洲中字在线 | 久久avav| 99这里| 午夜激情小视频 | 日本香蕉视频 | 99自拍网 | 中文字幕在线视频一区 | 91黄色在线视频 | www.污视频| 男人的天堂在线观看av | 久久精品观看 | 精品国产一级久久 | 日韩一区在线观看视频 | 日本亚洲国产 | 亚洲妇女无套内射精 | 国产私拍 | 黄色视屏网站 | 亚洲综合日韩精品欧美综合区 | 色偷偷av男人的天堂 | 亚洲国产成人无码av在线 | 欧美一区二区三区成人久久片 | 国产调教| 丝袜高跟av | 欧美aa大片 | 扒开腿揉捏花蒂h | 爱情岛论坛永久入口 | 九草视频在线观看 | 免费的黄色小视频 | 精品久久久久一区二区国产 | 又黄又爽视频在线观看 | 91蝌蚪少妇偷拍 | 黄色片在线免费看 | 韩国明星乱淫(高h)小说 | 成人国产a | 午夜窝窝 | 久久久区| 日韩性高潮 | 精品人妻少妇一区二区 | 爱爱色图 | 免费毛毛片 | 香蕉久久夜色精品国产使用方法 |