当新文件到达S3时,触发luigi任务

时间:2017-04-25 17:33:17

标签: python amazon-s3 scrapy luigi

我有一个带有新对象的存储桶,可以根据创建时间随机添加关键字。例如:

's3://my-bucket/mass/%s/%s/%s/%s/%s_%s.csv' % (time.strftime('%Y'), time.strftime('%m'), time.strftime('%d'), time.strftime('%H'), name, the_time)

事实上,这些是Scrapy爬行的输出。我想触发一个将这些爬网与我拥有的主.csv产品目录文件匹配的任务(称之为" product_catalog.csv"),该文件也会定期更新。

现在,我有几个Python脚本,我用全局变量编写,每次运行此过程时都会填写。那些需要成为导入属性。

所以这是需要发生的事情:

1)新的csv文件显示在" s3://my-bucket/mass/..."根据爬网完成的时间使用唯一的密钥名称。路易吉看到了这一点并开始了。  
2)" cleaning.py"由luigi在新文件上运行,因此" cleaning.py"的参数(在S3中显示的文件)需要在运行时提供给它。除了传递给下一步之外,结果将保存在S3中。
3)" product_catalog.csv"的最新版本从数据库中提取并使用" cleaning.py"的结果。 in" matching.py"

我意识到这可能没有完全意义。我将根据需要提供编辑,以使其更加清晰。

修改

根据初步答案,我已将此配置为拉动操作,可保存整个过程中的步骤。但现在我很迷茫。应该注意的是,这是我第一次将Python项目捆绑在一起,因此有些事情包括我正在学习的 init .py。像往常一样,在下一个障碍之后立即出现混乱,这是一条令人兴奋的坎坷之路。

以下是我的问题:
1)如何从Scrapy导入蜘蛛我不清楚。我有大约十几个,目标是让luigi管理抓取过程> clean>匹配所有这些。 Scrapy文档说包括:

class MySpider(scrapy.Spider):
    # Your spider definition

这是什么意思?在控制蜘蛛的脚本中重写蜘蛛?这没有任何意义,他们的例子没有帮助。

2)我已将Scrapy管道配置为导出到S3,但luigi似乎也使用output()执行此操作。我应该使用哪个以及如何让它们一起玩?

3)Luigi说CrawlTask​​()成功运行但这是错误的,因为它在几秒钟内完成,爬行通常需要几分钟。也没有与成功相对应的输出文件。

4)我在哪里提供S3的凭证?

这是我的代码。我已经评论了一些不能代替我认为更好的东西。但我的感觉是,我想要做的事情有一个宏伟的架构,我还不了解。

import luigi
from luigi.s3 import S3Target, S3Client
import my_matching
from datetime import datetime
import os
import scrapy
from twisted.internet import reactor
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from my_crawlers.my_crawlers.spiders import my_spider

class CrawlTask(luigi.Task):
    crawltime = datetime.now()
    spider = luigi.Parameter()
    #vertical = luigi.Parameter()

    def requires(self):
        pass

    def output(self):
        return luigi.LocalTarget("actual_data_staging/crawl_luigi_test_{}.csv".format(self.crawltime))
        #return S3Target("s3://my-bucket/mass/crawl_luigi_test_{}.csv".format(self.crawltime))

    def run(self):
        os.system("scrapy crawl %s" % self.spider)
        #process = CrawlerProcess(get_project_settings())
        #process.crawl("%s" % self.spider)
        #process.start()

class FetchPC(luigi.Task):
    vertical = luigi.Parameter()

    def output(self):
        if self.vertical == "product1":
            return "actual_data_staging/product1_catalog.csv"
        elif self.vertical == "product2":
            return "actual_data_staging/product2_catalog.csv"

class MatchTask(luigi.Task):
    crawltime = CrawlTask.crawltime
    vertical = luigi.Parameter()
    spider = luigi.Parameter()

    def requires(self):
        return CrawlTask(spider=self.spider)
        return FetchPC(vertical=self.vertical)

    def output(self):
        return luigi.LocalTarget("actual_data_staging/crawl_luigi_test_matched_{}.csv".format(self.crawltime))
        #return S3Target("s3://my-bucket/mass/crawl_luigi_test_matched_{}.csv".format(CrawlTask.crawltime))

    def run(self):
        if self.vertical == 'product1':
            switch_board(requires.CrawlTask(), requires.FetchPC())

MatchTask是指我编写的python脚本,它将已删除的产品与我的产品目录进行比较。它看起来像这样:

def create_search(value):
...
def clean_column(column):
...
def color_false_positive():
...
def switch_board(scrape, product_catalog):
# this function coordinates the whole script

2 个答案:

答案 0 :(得分:1)

下面是一个非常粗略的概述。我认为关于luigi作为拉动系统的大纲的主要区别在于,您首先指定所需的输出,然后触发输出所依赖的其他任务。因此,不是在爬网结束时命名事物,而是在开始时知道的事情之后更容易命名。可以通过其他方式实现,只需要很多不必要的复杂功能。

class CrawlTask(luigi.Task):
    crawltime = luigi.DateParameter()

    def requires(self):
        pass

    def get_filename(self):
        return "s3://my-bucket/crawl_{}.csv".format(self.crawltime)

    def output(self):
        return S3Target(self.get_filename())

    def run(self):
        perform_crawl(s3_filename=self.get_filename())


class CleanTask(luigi.Task):
    crawltime = luigi.DateParameter()

    def requires(self):
        return CrawlTask(crawltime=self.crawltime)

    def get_filename(self):
        return "s3://my-bucket/clean_crawl_{}.csv".format(self.crawltime)

    def output(self):
        return S3Target(self.get_filename())

    def run(self):
        perform_clean(input_file=self.input().path, output_filename=self.get_filename())


class MatchTask(luigi.Task):
    crawltime = luigi.DateParameter()

    def requires(self):
        return CleanTask(crawltime=self.crawltime)

    def output(self):
        return ##?? whatever output of this task is

    def run(self):
        perform_match(input_file=self.input().path)

答案 1 :(得分:0)

您可以做的是创建一个更大的系统,封装您的抓取和处理。这样您就不必检查s3是否有新对象。我之前没有使用过luigi,但也许你可以将你的scrapy工作变成一项任务,并且当它完成后你会完成你的处理任务。无论如何,我不会想到'检查' s3对于新东西是一个好主意,因为1.你将不得不使用大量的API调用,以及2.你需要编写一堆代码来检查某些东西是否是新的'或不,这可能会变得多毛。