在AWS Lambda上运行时,Scrapy会抛出错误ReactorNotRestartable

时间:2017-02-22 10:09:51

标签: amazon-web-services lambda scrapy twisted aws-lambda

我已经部署了一个scrapy项目,只要有一个lambda api请求就会抓取它。

它在第一次api调用时运行良好,但稍后失败并抛出ReactorNotRestartable错误。

据我所知,AWS Lambda生态系统没有杀死进程,因此reactor仍然存在于内存中。

lambda日志错误如下:

Traceback (most recent call last):
File "/var/task/aws-lambda.py", line 42, in run_company_details_scrapy
process.start()
File "./lib/scrapy/crawler.py", line 280, in start
reactor.run(installSignalHandlers=False)  # blocking call
File "./lib/twisted/internet/base.py", line 1242, in run
self.startRunning(installSignalHandlers=installSignalHandlers)
File "./lib/twisted/internet/base.py", line 1222, in startRunning
ReactorBase.startRunning(self)
File "./lib/twisted/internet/base.py", line 730, in startRunning
raise error.ReactorNotRestartable()
ReactorNotRestartable

lambda处理函数是:

def run_company_details_scrapy(event, context):
   process = CrawlerProcess()
   process.crawl(CompanyDetailsSpidySpider)
   process.start()

我通过在启动函数中插入标志来停止反应器

,从而解决了这个问题
process.start(stop_after_crawl=False)

但问题是我不得不等到lambda呼叫超时。

尝试了其他解决方案,但似乎没有一种解决方案。任何人都可以指导我如何解决这个问题。

3 个答案:

答案 0 :(得分:1)

此问题并非AWS Lambda独有 - 请参阅running a spider in a Celery task

您可以尝试ScrapyScript(披露:我写过)。它产生一个子进程来支持Twisted反应器,阻塞直到所有提供的蜘蛛都完成,然后退出。它是用Celery编写的,但用例类似。

在您的情况下,这应该有效:

from scrapyscript import Job, Processor
def run_company_details_scrapy(event, context):
    job = Job(CompanyDetailsSpidySpider())
    Processor().run(job)`

答案 1 :(得分:1)

最近有同样的问题,而且Crochet因为各种原因不想工作。

最终我们选择了肮脏的解决方案:只需在lambda处理函数结束时调用sys.exit(0)(或sys.exit(1),如果发现错误,而不是查看返回代码AFAICT)。这非常有效。

如果您打算从Lambda返回响应,显然没有任何好处,但如果您正在使用Scrapy,数据可能已经通过您的管道保留,使用调度程序作为Lambda的触发器,因此无需响应。

注意:您在CloudWatch中收到来自AWS的通知:

RequestId: xxxx Process exited before completing request 

答案 2 :(得分:0)

我在 AWS lambda 上遇到错误 ReactorNotRestartable,在我使用此解决方案后

默认情况下,scrapy 的异步特性不适用于 Cloud Functions,因为我们需要一种方法来阻止抓取以防止函数提前返回和实例在之前被杀死进程终止。

相反,我们可以使用 scrapydo 以阻塞方式运行您现有的蜘蛛:

import scrapy
import scrapy.crawler as crawler
rom scrapy.spiders import CrawlSpider
import scrapydo

scrapydo.setup()

# your spider
class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ['http://quotes.toscrape.com/tag/humor/']

    def parse(self, response):
        for quote in response.css('div.quote'):
            print(quote.css('span.text::text').extract_first())

scrapydo.run_spider(QuotesSpider)