前言Scrapy内置了很多命令,但是有时候我们想自定义一些命令,因为写脚本没有敲命令那么强大和方便。但是scrapy官网并没有自定义命令的文档,有的只是一句话:YoucanalsousetheCOMMANDS_MODULEsettingtoaddcustomprojectcommands。有关如何实现该命令的示例,请参阅scrapy/commands中的Scrapy命令。说白了,还是让我们自己看源码吧。脚本方式启动爬虫源码等。让我们看看如何在python文件中启动爬虫而不是使用scrapycrawlXXX。参见示例:importscrapyfromscrapy.crawlerimportCrawlerProcessclassMySpider(scrapy.Spider):#你的蜘蛛定义。..process=CrawlerProcess(settings={'FEED_FORMAT':'json','FEED_URI':'items.json'})process.crawl(MySpider)#process.crawl(MySpider1)可以运行多个,并且同时运行timeprocess.start()的参数,这里的CrawlerProcess是爬虫启动时的配置,应该和scrapycrawlXXX-o后的参数类似。也可以使用CrawlerRunner实现fromtwisted.internetimportreactorimportscrapyfromscrapy.crawlerimportCrawlerRunnerfromscrapy.utils.logimportconfigure_loggingclassMySpider(scrapy.Spider):...configure_logging({'LOG_FORMAT':'%(levelname)s:%(message)s'})runner=CrawlerRunner()d=runner.crawl(MySpider)d.addBoth(lambda_:reactor.stop())#关闭扭曲的反应器#d1=runner.crawl(MySpider1)#d1.addBoth(lambda_:reactor.stop())#当然也可以这样写:#runner.crawl(MySpider)#runner.crawl(MySpider1)#d=runner.join()#d.addBoth(lambda_:reactor.stop())reactor.run()如果不想同时运行,就像一个一个运行(另外,代码中的效果类似于pass):来自twisted.internetimportreactor,deferfromscrapy.crawlerimportCrawlerRunnerfromscrapy.utils.logimportconfigure_loggingclassMySpider1(scrapy.Spider):#你的第一个蜘蛛定义...classMySpider2(scrapy.Spider):#你的第二个蜘蛛定义...configure_logging()runner=CrawlerRunner()@defer.inlineCallbacksdefcrawl():yieldrunner.crawl(MySpider1)yieldrunner.crawl(MySpider2)reactor.stop()crawl()reactor.run()自定义命令crawall先看最常用的命令,运行所有爬虫文件crawl.py#-*-编码:utf-8-*-fromscrapy.commandsimportScrapyCommandclassCommand(ScrapyCommand):requires_project=Truedefsyntax(self):return'[options]'defshort_desc(self):return'运行所有的蜘蛛'defrun(self,args,opts):spider_list=self.crawler_process.spiders.list()fornameinspider_list:self.crawler_process.crawl(name,**opts.__dict__)self.crawler_process.start()这个是百度随便搜的一键就出来了,基本上代码是不会变的。在settings.py同级目录下创建commands文件夹,将crawall.py放入文件夹,然后添加COMMANDS_MODULE="newspider.commands"其中newspider为scrapy项目名,commands为我们创建的目录。然后就可以使用scrapycrawlall在命令行运行所有的爬虫了。假设需求假设现在有一个需求:写一个通用的爬虫抓取一些静态网页,数据解析部分可以由其他人完成,但是这里其他人不明白scrapy是什么,他们只负责写xpath和正则化。这样的爬虫使用scrapy应该很简单。第一个想法是给他们一个模板文件,让他们修改一些需要修改的内容,但是实际操作起来可能并不顺利,因为即使只需要修改部分内容,‘其他’也是这么看的很多代码还会说我不知道??该干什么,这样就没法完成任务,一不小心动了相关代码也不知道,不好管理。怎么做?可以简化模板文件,去掉代码部分只留下需要修改的内容字典(当然和字典不一样,某种约定好的格式就好,字典只是为了方便管理).例如:{'Title':['//title/text()',]},这个看起来很简单,只要让他们注意成对的括号和逗号即可。那么我们只需要根据这些创建爬虫文件即可,但是新的问题又出现了,他们如何测试自己的xpath是否写对了呢?不能让我们自己测试重写,这样效率太低了。终于引出正题了,有两种方式,一种是上面的自定义脚本,一种是自定义命令。虽然自定义脚本更简单,但这里还是使用自定义命令来说明如何使用自定义命令。命令效果:根据字典文件抓取相关内容,可以根据模板文件和字典文件创建爬虫文件,然后运行爬虫即可达到效果。这种效果就像是scrapygenspider(从模板创建爬虫)和scrapyrunspider(运行爬虫)的组合。所以我们直接看这两个命令的源码。代码太长就不上传了。可以去本地文件看文件(如果是anaconda,AnacondaLibsite-packagesscrapycommands)。看了之后发现genspider命令是使用string.Template的方式创建爬虫文件的,而且使用起来非常简单,其实等同于formatimportstringa='$adadafsfas$b'd={'a':1,'b':'dsada'}new_a=string.Template(a).substitute(d)看了runspider的代码,我们的命令可以这样写:utils.spider导入iter_spider_classesfromscrapy.commandsimportScrapyCommandfromUcrapy.exceptionslogor=ErrimportgetLogger(__name__)defcreate_spider(setting_rule,fname):d={'spidername':fname,'title':setting_rule.get('title')}withopen('../tempspider.py','r',encoding='utf-8')asf:tempstr=f.read()withopen(f'../spiders/{fname}_spider.py','w',encoding='utf-8')asfw:fw.write(string.Template(tempstr).substitute(d).replace('true','True').replace('false','假').replace('空','无'))def_import_file(filepath):abspath=os.path.abspath(filepath)dirname,file=os.path.split(abspath)logging.info(dirname)fname,fext=os.path.splitext(file)iffext!='.py':raiseValueError("NotaPythonsourcefile:%s"%abspath)ifdirname:sys.path=[dirname]+sys.pathtry:module=import_module(fname)除了Exceptionase:logger.error('模板文件可能有语言错误,请检查后重试!(%s)'%str(e))else:create_spider(module.setting_rule,fname)sys.path=[dirname+'/../spiders']+sys.pathspider_module=import_module(f'{fname}_spider')最后返回spider_module:ifdirname:sys.path.pop(0)sys.path.pop(0)classCommand(ScrapyCommand):requires_project=Truedefsyntax(self):return"
