爬虫启动与调度

前面我们运行爬虫时采用的都是scrapy命令行工具,这对于单纯的Scrapy爬虫工程来说是比较合理的选择。但有时情况更加复杂,我们可能需要将Scrapy集成到类似APScheduler的调度框架中,有时Scrapy可能只是一个大工程的子模块,我们需要在当前工程中调度爬虫。实际上Scrapy框架也提供了通过代码启动爬虫的API。

使用Python代码启动爬虫

CrawlerProcess

下面例子代码在当前进程中配置了quotesquotes2两个爬虫,并调用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调度爬虫

前面我们介绍了CrawlerProcessCrawlerRunner的区别,不幸的是,一旦你打算使用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()
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。