前言

虽然爬虫的入门级编写并不难,但要让爬虫真正稳定可靠的运行起来,真不是一件容易的事。
首先,要用到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)

此方法中的重要操作有:
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

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:\scrapy\scrapy.cfg',
xdg_config_home + '/scrapy.cfg',
os.path.expanduser('~/.scrapy.cfg')]
if use_closest:
sources.append(closest_scrapy_cfg())
return sources

init_env首先调用get_config()获取cfg配置文件,这个配置文件获取的优先级是:
1./etc/scrapy.cfg,c:\scrapy\scrapy.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

scrapy 源码解析 (一):启动流程源码分析(一)命令行启动的更多相关文章

  1. Android Activity启动流程源码全解析(1)

    前言 Activity是Android四大组件的老大,我们对它的生命周期方法调用顺序都烂熟于心了,可是这些生命周期方法到底是怎么调用的呢?在启动它的时候会用到startActivty这个方法,但是这个 ...

  2. Android Activity启动流程源码全解析(2)

    接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...

  3. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  4. 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码

    Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...

  5. Spark(四十九):Spark On YARN启动流程源码分析(一)

    引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...

  6. Spring IOC 容器预启动流程源码探析

    Spring IOC 容器预启动流程源码探析 在应用程序中,一般是通过创建ClassPathXmlApplicationContext或AnnotationConfigApplicationConte ...

  7. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  8. scrapy 源码解析 (二):启动流程源码分析(二) CrawlerProcess主进程

    CrawlerProcess主进程 它控制了twisted的reactor,也就是整个事件循环.它负责配置reactor并启动事件循环,最后在所有爬取结束后停止reactor.另外还控制了一些信号操作 ...

  9. ZooKeeper单机客户端的启动流程源码阅读

    客户端的启动流程 看上面的客户端启动的脚本图,可以看到,zookeeper客户端脚本运行的入口ZookeeperMain.java的main()方法, 关于这个类可以理解成它是程序启动的辅助类,由它提 ...

随机推荐

  1. Redis学习笔记(二十一) 事务

    文章开始啰嗦两句,写到这里共21篇关于redis的琐碎知识,没有过多的写编程过程中redis的应用,着重写的是redis命令.客户端.服务器以及生产环境搭建用到的主从.哨兵.集群实现原理,如果你真的能 ...

  2. python基础003----标准数据类型

    一.标准数据类型 在python中,只要定义了一个变量,而且它有数据,那么它的类型就已经确定了,不需要开发者主动去说明它的类型,系统会自动识别.可以用type(变量名)来查看变量的类型.常见的变量类型 ...

  3. java锁总结

    1.公平锁与非公平锁 公平锁:指多个线程在等待同一个锁时,必须按照申请锁的先后顺序来依次获得锁. 优点:等待锁的线程不会饿死.缺点:整体效率相对较低. 非公平锁:可以抢占,即如果在某个时刻有线程需要获 ...

  4. JDBC——什么是JDBC?

    JDBC:Java数据库连接(Java DataBase Connectivity),是Java语言中用来规范客户端如何程序如何来访问数据库的应用程序接口(API),提供了诸如查询和更新数据库中数据的 ...

  5. C语言宏技巧 X宏

    前言 本文介绍下X宏的使用 首先简单介绍下宏的几种用法 #define STRCAT(X,Y) X##Y #define _STR(X) #@X #define STR(X) #X #define L ...

  6. WeChair项目Beta冲刺(8/10)

    团队项目进行情况 1.昨日进展    Beta冲刺第八天 昨日进展: 前后端并行开发,项目按照计划有条不絮进行 2.今日安排 前端:扫码占座功能和预约功能并行开发 后端:扫码占座后端逻辑开发,编码使用 ...

  7. C语言实现类

    #ifndef __DEFINE__H__ #define __DEFINE__H__ #define vector3(type) \ typedef struct vector3_##type { ...

  8. 从零开始手把手教你使用原生JS+CSS3实现幸运水果机游戏

    项目体验地址 免费视频教程 游戏介绍 幸运水果机是一款街机游戏,游戏界面由24个方格拼接成一个正方形,每个方格中都有一个不同的水果图形,方格下都有一个小灯.玩家使用游戏币选择希望押注的目标,按下开始后 ...

  9. Project Loom:Reactive模型和协程进行时(翻译)

    Java 15将发布Project Loom的第一个版本.我相信这将改变JVM.在这篇文章中,我想深入探讨一下导致我相信这一点的原因. 首先,我们需要了解核心问题.然后,我将尝试描述以前的技术如何解决 ...

  10. Spring事务方法上增加synchronized真的有效果吗?

    此文转载,Spring事务本身是一个非常复制的问题,再加上线程并发处理就更加要主要了,由于再开发中有很多朋友会范与下文同样的错误,因分享给大家. 前言 Spring事务的一个奇怪的问题. 朋友问了我一 ...