如何使用非内置存储 URI 参数在 Scrapy 中自定义 URI

How to customize URI in Scrapy with non built in storage URI parameters

我想将 Scrapy 提要 URI 自定义为 s3 以包含上传文件的尺寸。目前我在 settings.py 文件中有以下内容:

FEEDS = {
    's3://path-to-file/file_to_have_dimensions.csv': {
        'format': 'csv',
        'encoding': 'utf8',
        'store_empty': False,
        'indent': 4,
    }
}

但是想要像下面这样的东西:

NUMBER_OF_ROWS_IN_CSV = file.height()
    FEEDS = {
        f's3://path-to-files/file_to_have_dimensions_{NUMBER_OF_ROWS_IN_CSV}.csv': {
            'format': 'csv',
            'encoding': 'utf8',
            'store_empty': False,
            'indent': 4,
        }
    }

请注意,我希望自动插入行数。

仅通过更改 settings.py 是否可以做到这一点,还是需要更改 scrapy 代码的其他部分?

提要文件是在蜘蛛程序启动时创建的 运行ning,此时项目的数量尚不清楚。但是,当蜘蛛完成 运行ning 时,它会调用一个名为 closed 的方法,您可以从该方法访问蜘蛛统计信息、设置,还可以执行您想要 运行 的任何其他任务在蜘蛛完成抓取和保存项目后。

在下面的例子中,我将提要文件从 intial_file.csv 重命名为 final_file_{item_count}.csv

因为你不能在 s3 中重命名文件,我使用 boto3 库将 initial_file 复制到一个新文件并使用文件名中包含的 item_count 值命名它然后删除初始文件。

import scrapy
import boto3

class SampleSpider(scrapy.Spider):
    name = 'sample'
    start_urls = [
        'http://quotes.toscrape.com/',
    ]

    custom_settings = {
        'FEEDS': {
            's3://path-to-file/initial_file.csv': {
                'format': 'csv',
                'encoding': 'utf8',
                'store_empty': False,
                'indent': 4,
            }
        }
    }

    def parse(self, response):
        for quote in response.xpath('//div[@class="quote"]'):
            yield {
                'text': quote.xpath('./span[@class="text"]/text()').extract_first(),
                'author': quote.xpath('.//small[@class="author"]/text()').extract_first(),
                'tags': quote.xpath('.//div[@class="tags"]/a[@class="tag"]/text()').extract()
            }

    def closed(self, reason):
        item_count = self.crawler.stats.get_value('item_scraped_count')
        try:
            session = boto3.Session(aws_access_key_id = 'awsAccessKey', aws_secret_access_key = 'awsSecretAccessKey')
            s3 = session.resource('s3')
            s3.Object('my_bucket', f'path-to-file/final_file_{item_count}.csv').copy_from(CopySource = 'my_bucket/path-to-file/initial_file.csv')
            s3.Object('my_bucket', 'path-to-file/initial_file.csv').delete()
        except:
            self.logger.info("unable to rename s3 file")