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

bnu*_*sey 10 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))
Run Code Online (Sandbox Code Playgroud)

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

pbs*_*mar 17

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

  1. 选择一个Item Exporter类并将其导入到pipeline.py项目目录中.它可以是预定义的项目导出程序(例如XmlItemExporter)或用户定义的(如FanItemExporter问题中定义的)
  2. 在中创建一个Item Pipeline类pipeline.py.在此类中实例化导入的项目导出器.细节将在答案的后半部分解释.
  3. 现在,在settings.py文件中注册此管道类.

以下是每个步骤的详细说明.每个步骤都包含问题的解决方案.

步骤1

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

  • 如果需要自定义导出器,请在文件中定义自定义类.我建议把课程放在exporters.py档案中.将此文件放在项目文件夹中(where settings.py,items.pyreside).

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

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

版本1:扩展BaseItemExporter

既然BaseItemExporter是父类,start_exporting(),finish_exporting(),export_item()必须overrided,以满足我们的需要.

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)))
Run Code Online (Sandbox Code Playgroud)

版本2:扩展JsonLinesItemExporter

JsonLinesItemExporter提供完全相同的export_item()方法实现.因此,只有start_exporting()finish_exporting()方法overrided.

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]}')
Run Code Online (Sandbox Code Playgroud)

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

第2步

创建Item Pipeline类.

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
Run Code Online (Sandbox Code Playgroud)

第3步

由于定义了项目导出管道,因此请在settings.py文件中注册此管道.还要将字段添加FILE_NAMEsettings.py文件中.该字段包含输出文件的文件名.

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

FILE_NAME = 'path/outputfile.ext'
ITEM_PIPELINES = {
    'project_name.pipelines.FanExportPipeline' : 600,
}
Run Code Online (Sandbox Code Playgroud)

如果ITEM_PIPELINES已取消注释,则将以下行添加到ITEM_PIPELINES字典中.

'project_name.pipelines.FanExportPipeline' : 600,

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

注意:

  • 我在指南中看到了类似的例子,但是使用了内置的导出器.但是,总是使用管道有点多余.有没有办法绕过它? (2认同)
  • @Lore这里现在是一种没有自定义管道的方法/sf/answers/5360172681/抱歉necroposting:) (2认同)