首页 > 解决方案 > 在scrapy CSV导出管道中关闭打开的csv文件

问题描述

我正在尝试抓取 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

标签: pythonscrapyscrapy-pipeline

解决方案


The problem probably is that you keep all the ItemExporters and files open until the spider closes. I suggest that you should try to close the CsvItemExporter and corresponding file for the previous company before you open a new one.

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]

推荐阅读