作业:暂停并恢复爬行

有时,对于一些大型网站,需要中途暂停并在某个时间继续爬取。

使用以下工具,Scrapy可以实现这种开箱即用的这种功能,

  • 将请求列表保存到磁盘的一种调度程序
  • 在磁盘上保存已经请求过的一种过滤器
  • 一个能持续保持爬虫状态(键/值对)的扩展

Job 路径

要启动持久化功能你只需通过 JOBDIR 设置一个 job directory 选项。这个路径会存储 所有的请求数据来保持一个单独任务的状态(如:一次爬虫)。需要注意的是,这个路径不能被 不同的爬虫之共享,或者一个爬虫中的不同的 jobs/runs。这意味着它只能保存一个 单独 任务的 状态。

如何使用

启用持久功能来开始一个爬虫, 按照以下方式运行:

scrapy crawl somespider -s JOBDIR=crawls/somespider-1

然后,你可以在任何时间使用安全方式停止爬虫 (使用 Ctrl-C 或者发送一个 信号), 并可以使用相同的命令来继续爬虫:

scrapy crawl somespider -s JOBDIR=crawls/somespider-1

保持状态

有时你想保持一些爬虫的状态。你可以使用 spider.state 属性来实现,这个属性必需是 一个字典。Scrapy提供了一个内置扩展负责在爬虫开始和停止的时候从 job路径 序列化、存储并加载这个属性。

这里有一个例子使用了爬虫状态来进行回调(简洁起见省略了其它爬虫代码):

def parse_item(self, response):
    # parse item here
    self.state['items_count'] = self.state.get('items_count', 0) + 1

持久化的一些事项

如果你想启用Scrapy持久化,以下几条你需要注意:

Cookies 有效期

Cookies 会过期。 因此,如果你没有尽快恢复爬虫,请求请求调度器可能不再工作。 如果你的爬虫不依赖cookies这也不是问题。

请求序列化

请求需要被 pickle 模块序列化,你需要确保你的请求可以被序列化以确保可以 持续运行。

最常见的问题是在requests回调函数中使用 lambda 函数,导致无法被序列化。

以下操作将无法工作:

def some_callback(self, response):
    somearg = 'test'
    return scrapy.Request('http://www.example.com', callback=lambda r: self.other_callback(r, somearg))

def other_callback(self, response, somearg):
    print("the argument passed is: %s" % somearg)

但是可以这样:

def some_callback(self, response):
    somearg = 'test'
    return scrapy.Request('http://www.example.com', callback=self.other_callback, meta={'somearg': somearg})

def other_callback(self, response):
    somearg = response.meta['somearg']
    print("the argument passed is: %s" % somearg)

如果你想记录这些没有被序列化的请求, 你可以在项目中设置 SCHEDULER_DEBUG 选项为 True 。 它默认是 False