爬虫启动与调度
前面我们运行爬虫时采用的都是scrapy
命令行工具,这对于单纯的Scrapy爬虫工程来说是比较合理的选择。但有时情况更加复杂,我们可能需要将Scrapy集成到类似APScheduler的调度框架中,有时Scrapy可能只是一个大工程的子模块,我们需要在当前工程中调度爬虫。实际上Scrapy框架也提供了通过代码启动爬虫的API。
使用Python代码启动爬虫
CrawlerProcess
下面例子代码在当前进程中配置了quotes
和quotes2
两个爬虫,并调用process.start()
全部启动,它们是并发运行的。
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
if __name__ == '__main__':
process = CrawlerProcess(get_project_settings())
process.crawl('quotes')
process.crawl('quotes2')
process.start()
代码中,get_project_settings()
会从当前Scrapy项目中读取scrapy.cfg
配置,process.start()
会阻塞直到所有爬虫运行完成。
此外,如果Spider还有自定义参数,我们直接在process.crawl()
方法中添加命名参数即可。
process.crawl('quotes', category='humor', limit=10)
CrawlerProcess
是Scrapy的核心组件,用于在一个进程中同时运行多个爬虫。CrawlerProcess
继承自CrawlerRunner
,但它还会额外配置全局日志记录、开启Twisted的事件循环、处理系统信号(例如Ctrl+C)以优雅关闭爬虫,如果我们没有其它的Twisted事件循环,那么使用CrawlerProcess
是合适的。
CrawlerRunner
然而,如果我们的程序中已经开启了Twisted事件循环(比如还在使用基于Twisted的调度框架等情况),此时就需要直接调用CrawlerRunner
,这样代码可能会复杂许多,采用这种比较底层的调度方式需要你对异步非阻塞并发编程和Twisted框架有一定的了解。下面例子中,我们直接调用CrawlerRunner
,但额外配置了日志,并手动编写了在两个爬虫执行结束后关闭Twisted事件循环的逻辑。
import logging
from scrapy.crawler import CrawlerRunner
from scrapy.utils.project import get_project_settings
from twisted.internet import reactor, defer
if __name__ == '__main__':
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(name)s] %(levelname)s: %(message)s',
)
runner = CrawlerRunner(get_project_settings())
@defer.inlineCallbacks
def crawl():
yield defer.DeferredList([
runner.crawl('quotes'),
runner.crawl('quotes2'),
])
reactor.stop()
crawl()
reactor.run()
使用APScheduler调度爬虫
前面我们介绍了CrawlerProcess
和CrawlerRunner
的区别,不幸的是,一旦你打算使用APScheduler调度爬虫就不得不用CrawlerRunner
,为了把APScheduler和Scrapy整合到一起,我们需要使用APScheduler的TwistedScheduler
调度器并手动启动Twisted事件循环。
最新版本的Scrapy(2.13.2)默认需要使用AsyncioSelectorReactor
,但我实际测试后发现这和APScheduler(3.11.0)框架的TwistedScheduler
整合到一起后会产生问题,爬虫会无法执行,因此我们这里手动配置Scrapy使用传统的SelectReactor
。虽然它的并发性能不如AsyncioSelectorReactor
,但前面我们说过,Scrapy爬虫本身是个客户端程序,它对于并发性能的要求一般需求下没有那么高,SelectReactor
通常也足够使用了。不过随着时间的推移,未来情况可能发生变化。
TWISTED_REACTOR = 'twisted.internet.selectreactor.SelectReactor'
下面代码我们在TwistedScheduler
调度器中注册了两个爬虫任务,最后启动调度器和Twisted事件循环,此时我们的两个爬虫就可以被正确的调度执行了。
import logging
from twisted.internet import reactor
from scrapy.crawler import CrawlerRunner
from scrapy.utils.project import get_project_settings
from apscheduler.schedulers.twisted import TwistedScheduler
if __name__ == '__main__':
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(name)s] %(levelname)s: %(message)s',
)
runner = CrawlerRunner(get_project_settings())
# 初始化调度器并添加任务
scheduler = TwistedScheduler()
scheduler.add_job(lambda: runner.crawl('quotes'), 'interval', seconds=10)
scheduler.add_job(lambda: runner.crawl('quotes2'), 'interval', seconds=5)
# 启动调度器
scheduler.start()
# 启动事件循环
reactor.run()