如何创建自定义Scrapy项目导出程序?

时间:2015-10-22 21:15:08

标签: python json scrapy

我正在尝试基于JsonLinesItemExporter创建一个自定义Scrapy项目导出器,这样我就可以稍微改变它产生的结构。

我已阅读此处的文档http://doc.scrapy.org/en/latest/topics/exporters.html,但未说明如何创建自定义导出程序,存储位置或如何将其链接到管道。

我已经确定了如何使用Feed Exporters进行自定义,但这不符合我的要求,因为我想从我的管道中调用此导出器。

以下是我提出的代码,该代码已存储在名为exporters.py的项目根目录中的文件中

from scrapy.contrib.exporter import JsonLinesItemExporter

class FanItemExporter(JsonLinesItemExporter):

def __init__(self, file, **kwargs):
    self._configure(kwargs, dont_fail=True)
    self.file = file
    self.encoder = ScrapyJSONEncoder(**kwargs)
    self.first_item = True

def start_exporting(self):
    self.file.write("""{
'product': [""")

def finish_exporting(self):
    self.file.write("]}")

def export_item(self, item):
    if self.first_item:
        self.first_item = False
    else:
        self.file.write(',\n')
    itemdict = dict(self._get_serialized_fields(item))
    self.file.write(self.encoder.encode(itemdict))

我只是尝试通过使用FanItemExporter并尝试导入的变体来从我的管道中调用它,但它不会产生任何结果。

1 个答案:

答案 0 :(得分:15)

Scrapy文档确实没有明确说明项目导出器的放置位置。要使用项目导出器,请遵循以下步骤。

  1. 选择一个Item Exporter类并将其导入项目目录中的pipeline.py。它可以是预定义的项目导出程序(例如XmlItemExporter)或用户定义的(如问题中定义的FanItemExporter
  2. pipeline.py中创建项目管道类。在此类中实例化导入的项目导出器。详情将在答案的后半部分解释。
  3. 现在,在settings.py文件中注册此管道类。
  4. 以下是每个步骤的详细说明。每个步骤都包含问题的解决方案。

    第1步

    • 如果使用预定义的Item Exporter类,请从scrapy.exporters模块导入它 例如: from scrapy.exporters import XmlItemExporter

    • 如果需要自定义导出器,请在文件中定义自定义类。我建议将该课程放在exporters.py文件中。将此文件放在项目文件夹中(settings.pyitems.py所在的位置)。

      在创建新的子类时,导入BaseItemExporter始终是个好主意。如果我们打算完全改变功能,那将是恰当的。但是,在这个问题中,大多数功能都接近JsonLinesItemExporter

    因此,我附加了相同ItemExporter的两个版本。一个版本扩展BaseItemExporter类,另一个版本扩展JsonLinesItemExporter

    版本1 :扩展BaseItemExporter

    由于BaseItemExporter是父类,start_exporting()finish_exporting()export_item()必须覆盖以满足我们的需求。

    from scrapy.exporters import BaseItemExporter
    from scrapy.utils.serialize import ScrapyJSONEncoder
    from scrapy.utils.python import to_bytes
    
    class FanItemExporter(BaseItemExporter):
    
        def __init__(self, file, **kwargs):
            self._configure(kwargs, dont_fail=True)
            self.file = file
            self.encoder = ScrapyJSONEncoder(**kwargs)
            self.first_item = True
    
        def start_exporting(self):
            self.file.write(b'{\'product\': [')
    
        def finish_exporting(self):
            self.file.write(b'\n]}')
    
        def export_item(self, item):
            if self.first_item:
                self.first_item = False
            else:
                self.file.write(b',\n')
            itemdict = dict(self._get_serialized_fields(item))
            self.file.write(to_bytes(self.encoder.encode(itemdict)))
    

    第2版:扩展JsonLinesItemExporter

    JsonLinesItemExporter提供了与export_item()方法完全相同的实现。因此,只有start_exporting()finish_exporting()方法被覆盖。

    JsonLinesItemExporter

    文件夹中可以看到python_dir\pkgs\scrapy-1.1.0-py35_0\Lib\site-packages\scrapy\exporters.py的实施情况
    from scrapy.exporters import JsonItemExporter
    
    class FanItemExporter(JsonItemExporter):
    
        def __init__(self, file, **kwargs):
            # To initialize the object using JsonItemExporter's constructor
            super().__init__(file)
    
        def start_exporting(self):
            self.file.write(b'{\'product\': [')
    
        def finish_exporting(self):
            self.file.write(b'\n]}')
    

    注意:将数据写入文件时,请务必注意标准的Item Exporter类需要二进制文件。因此,必须以二进制模式(b)打开文件。出于同样的原因,版本中的write()方法写入文件bytes

    第2步

    创建项目管道类。

    from project_name.exporters import FanItemExporter
    
    class FanExportPipeline(object):
        def __init__(self, file_name):
            # Storing output filename
            self.file_name = file_name
            # Creating a file handle and setting it to None
            self.file_handle = None
    
        @classmethod
        def from_crawler(cls, crawler):
            # getting the value of FILE_NAME field from settings.py
            output_file_name = crawler.settings.get('FILE_NAME')
    
            # cls() calls FanExportPipeline's constructor
            # Returning a FanExportPipeline object
            return cls(output_file_name)
    
        def open_spider(self, spider):
            print('Custom export opened')
    
            # Opening file in binary-write mode
            file = open(self.file_name, 'wb')
            self.file_handle = file
    
            # Creating a FanItemExporter object and initiating export
            self.exporter = FanItemExporter(file)
            self.exporter.start_exporting()
    
        def close_spider(self, spider):
            print('Custom Exporter closed')
    
            # Ending the export to file from FanItemExport object
            self.exporter.finish_exporting()
    
            # Closing the opened output file
            self.file_handle.close()
    
        def process_item(self, item, spider):
            # passing the item to FanItemExporter object for expoting to file
            self.exporter.export_item(item)
            return item
    

    第3步

    由于定义了项目导出管道,请在settings.py文件中注册此管道。同时将字段FILE_NAME添加到settings.py文件。该字段包含输出文件的文件名。

    将以下行添加到settings.py文件中。

    FILE_NAME = 'path/outputfile.ext'
    ITEM_PIPELINES = {
        'project_name.pipelines.FanExportPipeline' : 600,
    }
    

    如果ITEM_PIPELINES已取消注释,则将以下行添加到ITEM_PIPELINES词典。

    'project_name.pipelines.FanExportPipeline' : 600,

    这是创建自定义项目导出管道的一种方法。

    注意