【问题标题】:Close open csv file in scrapy CSV Export Pipeline在scrapy CSV导出管道中关闭打开的csv文件
【发布时间】:2020-05-26 05:43:08
【问题描述】:

我正在尝试抓取 100 家公司的文章,我想将多篇文章的内容保存到每个公司的单独 csv 文件中。我已经构建了刮刀和一个 csv 导出管道,它工作正常,但是,蜘蛛为每个公司(应该)打开一个新的 csv 文件,而不关闭为前一个公司打开的文件。

csv 文件在蜘蛛关闭后关闭,但由于我为每个公司抓取的数据量很大,文件大小很大,会对我的机器内存造成压力,并且无法实际扩展,如果我想要的话为了增加公司的数量(我最终想做的事情),我最终会因为一次打开太多文件而遇到错误。下面是我的 csv 导出器管道。我想找到一种方法来关闭当前公司的一个 csv 文件,然后再转到同一蜘蛛中的下一家公司:

我想,理论上,我可以为每篇文章打开文件,将内容写入新行,然后关闭它并为下一篇文章再次打开它,但这会显着减慢蜘蛛的速度。我想在蜘蛛仍在浏览该公司的文章时为给定公司保持文件打开状态,然后在蜘蛛移动到下一家公司时将其关闭。

我确信有一个解决方案,但我一直无法弄清楚。非常感谢帮助解决这个问题。

class PerTickerCsvExportPipeline:
    """Distribute items across multiple CSV files according to their 'ticker' field"""

    def open_spider(self, spider):
        self.ticker_to_exporter = {}

    def close_spider(self, spider):
        for exporter in self.ticker_to_exporter.values():
            exporter.finish_exporting()

    def _exporter_for_item(self, item):
        ticker = item['ticker']
        if ticker not in self.ticker_to_exporter:
            f = open('{}_article_content.csv'.format(ticker), 'wb')
            exporter = CsvItemExporter(f)
            exporter.start_exporting()
            self.ticker_to_exporter[ticker] = exporter
        return self.ticker_to_exporter[ticker]

    def process_item(self, item, spider):
        exporter = self._exporter_for_item(item)
        exporter.export_item(item)
        return item

【问题讨论】:

    标签: python scrapy scrapy-pipeline


    【解决方案1】:

    问题可能是你保持所有ItemExporters 和文件打开直到蜘蛛关闭。我建议您在打开新公司之前尝试关闭之前公司的CsvItemExporter 和相应文件。

    def open_spider(self, spider):
        self.ticker_to_exporter = {}
        self.files = []
    
    def close_exporters(self):
        for ticker, exporter in self.ticker_to_exporter.items():
            exporter.finish_exporting()
            del self.ticker_to_exporter[ticker]
    
    def close_files(self):
        for i, f in enumerate(self.files):
            f.close()
            del self.files[i]
    
    def close_spider(self, spider):
        self.close_exporters()
        self.close_files()
    
    def _exporter_for_item(self, item):
        ticker = item['ticker']
        if ticker not in self.ticker_to_exporter:
            self.close_exporters()
            self.close_files()
            f = open('{}_article_content.csv'.format(ticker), 'a')
            self.files.append(f)
            exporter = CsvItemExporter(f)
            exporter.start_exporting()
            self.ticker_to_exporter[ticker] = exporter
        return self.ticker_to_exporter[ticker]
    

    【讨论】:

    • 我试过了,蜘蛛运行时文件仍然保持打开状态。之后我尝试将 self.close_exporters() 语句添加到 close_exporters() 方法中,但不幸的是仍然遇到了同样的问题。
    • @as_owl 也许你也应该关闭这些文件。我扩展了我的答案,请尝试它是否适合您。
    • 我相信我需要在 self.files.append() 语句中将 f 附加到 self.files,不是吗?截至目前, self.files.append() 语句没有做任何事情......现在测试它,它会让你知道它是否有效!谢谢
    • Patrick,不幸的是,您对代码所做的编辑有一个令人讨厌的副作用,因为它只会将最近抓取的文章内容保存到 csv,即每次迭代都会覆盖文件在蜘蛛。我想知道,不是一次全部导出抓取的内容,而是使用 csv 编写器写入新行,但这可能会减慢蜘蛛打开和关闭文件并向其添加新数据的速度每次迭代。
    • @as_owl true,可能与the way the files打开有关。此外,Scrapy 不会按照您可能期望的顺序返回项目,因为它不会一个一个地处理请求而是并发的,因此来自不同类别的项目是无序的,因此文件会被重新排序/覆盖。是的,你是对的,你应该附加f。不知道我是怎么错过的,谢谢指出!我已经更改了打开部分和附加部分。
    猜你喜欢
    • 2015-07-08
    • 2014-10-07
    • 2015-10-10
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    • 2018-07-17
    • 1970-01-01
    • 2011-07-11
    相关资源
    最近更新 更多