zoukankan      html  css  js  c++  java
  • scrapy 源码解析 (一):启动流程源码分析(一)命令行启动

    前言

    虽然爬虫的入门级编写并不难,但要让爬虫真正稳定可靠的运行起来,真不是一件容易的事。
    首先,要用到scrapy,就必须要读懂scrapy这个爬虫框架,如果连这个框架的执行逻辑都搞不懂,那么爬虫也很难写好。

    1.命令行启动

    这里先不谈使用了各种框架的复杂情况,比如scrapyd服务、redis分布式队列等。只看最简单的情况,假设只写了几个简单爬虫spider(中间件和管道写不写无影响)。

    输入命令

    通过命令行运行其中的某一个spider:

    scrapy crawl myspider

    首先,命令里的scrapy是一个可执行文件,后面的crawl myspider是scrapy的参数。
    可执行文件scrapy在/usr/local/python/bin目录里,是一个python脚本,有效代码为:

    from scrapy.cmdline import execute
    if __name__ == '__main__':
        sys.argv[0] = re.sub(r'(-script.pyw?|.exe)?$', '', sys.argv[0])
        sys.exit(execute())

    这个文件的作用就是从命令行里读取命令,然后传递给scrapy.cmdline包的execute()方法进行下一步操作。

    execute()方法

    从python的第三方库目录里找到scrapy/cmdline.py文件,可以看到代码中有execute()方法(为了压缩篇幅,这里删掉了无关紧要的代码):

    def execute(argv=None, settings=None):
        if argv is None:
            argv = sys.argv
            
        if settings is None:
            settings = get_project_settings()
            # set EDITOR from environment if available
            try:
                editor = os.environ['EDITOR']
            except KeyError: pass
            else:
                settings['EDITOR'] = editor
        check_deprecated_settings(settings)
    
        inproject = inside_project()
        cmds = _get_commands_dict(settings, inproject)
        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)
    
        cmd = cmds[cmdname]
        parser.usage = "scrapy %s %s" % (cmdname, cmd.syntax())
        parser.description = cmd.long_desc()
        settings.setdict(cmd.default_settings, priority='command')
        cmd.settings = settings
        cmd.add_options(parser)
        opts, args = parser.parse_args(args=argv[1:])
        _run_print_help(parser, cmd.process_options, args, opts)
    
        cmd.crawler_process = CrawlerProcess(settings)
        _run_print_help(parser, _run_command, cmd, args, opts)
        sys.exit(cmd.exitcode)
        
    def _run_command(cmd, args, opts):
        if opts.profile:
            _run_command_profiled(cmd, args, opts)
        else:
            cmd.run(args, opts)
    View Code

    此方法中的重要操作有:
    1.执行get_project_settings()方法,导入全局配置文件scrapy.cfg,进而导入项目的settings .py。这里用到了scrapy/utils/project.py中的get_project_settings()方法,这里先不展开。
    2.解析命令行scrapy之后的命令,并把命令中的配置项导入到settings中,中间的一大段代码就是执行此功能,每个有效的命令均对应scrapy/commands中以命令命名的文件中的Command类。
    3.cmd.crawler_process = CrawlerProcess(settings)这句代码建立CrawlerProcess对象,并把所有的设置settings传递给此对象。
    4._run_print_help(parser, _run_command, cmd, args, opts)调用_run_command执行命令。如果指定了profile命令行参数,则用cProfile运行命令,cProfile是一个标准模块,这里不必考虑。无论如何,最后都会执行命令的run()方法,也就是scrapy/commands/crawl.py中Command类的run()方法。

    执行crawl命令

    scrapy/commands/crawl.py#Command:

        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()

    这里有2个重要操作:
    1.调用CrawlerProcess的crawl方法,执行初始化(创建新爬虫对象)。
    2.调用CrawlerProcess的start方法,正式运行。
    CrawlerProcess便是scrapy运行过程中最根本的进程,是所有爬虫运行的基础。

    附:get_project_settings()导入配置文件的过程

    scrapy/utils/project.py:

    ...
    ENVVAR = 'SCRAPY_SETTINGS_MODULE'
    ...
    def get_project_settings():
        if ENVVAR not in os.environ:
            project = os.environ.get('SCRAPY_PROJECT', 'default')
            init_env(project)
    
        settings = Settings()
        settings_module_path = os.environ.get(ENVVAR)
        if settings_module_path:
            settings.setmodule(settings_module_path, priority='project')
    
        # XXX: remove this hack
        pickled_settings = os.environ.get("SCRAPY_PICKLED_SETTINGS_TO_OVERRIDE")
        if pickled_settings:
            settings.setdict(pickle.loads(pickled_settings), priority='project')
    
        # XXX: deprecate and remove this functionality
        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
    View Code

    get_project_settings会首先判断是否设置了SCRAPY_SETTINGS_MODULE环境变量,这个环境变量用来指定工程的配置模块。如果没有这个环境变量,则会调用init_env来初始化环境变量,由于我们没有设置SCRAPY_PROJECT,所以会用default默认值来执行init_env(在scrapy/utils/conf.py)。

    scrapy/utils/conf.py:

    def init_env(project='default', set_syspath=True):
        """Initialize environment to use command-line tool from inside a project
        dir. This sets the Scrapy settings module and modifies the Python path to
        be able to locate the project module.
        """
        cfg = get_config()
        if cfg.has_option('settings', project):
            os.environ['SCRAPY_SETTINGS_MODULE'] = cfg.get('settings', project)
        closest = closest_scrapy_cfg()
        if closest:
            projdir = os.path.dirname(closest)
            if set_syspath and projdir not in sys.path:
                sys.path.append(projdir)
    
    def get_config(use_closest=True):
        """Get Scrapy config file as a SafeConfigParser"""
        sources = get_sources(use_closest)
        cfg = SafeConfigParser()
        cfg.read(sources)
        return cfg
    
    def get_sources(use_closest=True):
        xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or 
            os.path.expanduser('~/.config')
        sources = ['/etc/scrapy.cfg', r'c:scrapyscrapy.cfg',
                   xdg_config_home + '/scrapy.cfg',
                   os.path.expanduser('~/.scrapy.cfg')]
        if use_closest:
            sources.append(closest_scrapy_cfg())
        return sources
    View Code

    init_env首先调用get_config()获取cfg配置文件,这个配置文件获取的优先级是:
    1./etc/scrapy.cfg,c:scrapyscrapy.cfg
    2.XDG_CONFIG_HOME环境变量指定的目录下的scrapy.cfg
    3.~/.scrapy.cfg
    4.当前执行目录下的scrapy.cfg或者父目录中的scrapy.cfg
    由于1,2,3默认都不设置,所以就使用当前执行命令下的scrapy.cfg,也就是工程目录下的scrapy.cfg。这个文件的内容很简单:

    [settings]
    default = myspider.settings
    [deploy]
    #url = http://localhost:6800/
    project = myspider

    根据default = myspider.settings找到对应的配置模块,后面会执行一系列导入settings.py配置项的操作,过程其实很复杂,这里不再详细说了。

    ————————————————
    版权声明:本文为CSDN博主「csdn_yym」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/csdn_yym/java/article/details/85420528

  • 相关阅读:
    安装mysql apache php smb
    解决IE8placeholder属性问题
    将博客搬至CSDN
    shell脚本获取配置文件中的内容
    shell sed指令全解
    salt一键部署hive
    salt一键部署habse
    salt一键部署kafka
    salt一键部署elasticsearch
    salt一键部署mysql
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12930422.html
Copyright © 2011-2022 走看看