Lzh on GitHub

一旦你抓取到项目,你通常希望持久化或导出这些项目,以便在其他应用程序中使用这些数据。毕竟,这是抓取过程的全部目的。

为此,Scrapy 提供了一系列用于不同输出格式(如 XML、CSV 或 JSON)的项目导出器。

使用项目导出器

如果你很着急,只想使用项目导出器输出抓取到的数据,请参阅 Feed 导出。否则,如果你想了解项目导出器的工作原理或需要更自定义的功能(默认导出未涵盖),请继续阅读以下内容。

要使用项目导出器,你必须使用其必需参数实例化它。每个项目导出器需要不同的参数,因此请查看每个导出器文档以确保,具体请参阅内置项目导出器参考。实例化导出器后,你需要:

  1. 调用 start_exporting() 方法以指示导出过程的开始
  2. 对要导出的每个项目调用 export_item() 方法
  3. 最后调用 finish_exporting() 以指示导出过程的结束

在这里,你可以看到一个项目管道,它使用多个项目导出器根据其中一个字段的值将抓取到的项目分组到不同的文件中:

from itemadapter import ItemAdapter
from scrapy.exporters import XmlItemExporter

class PerYearXmlExportPipeline:
    """根据项目的“year”字段将项目分发到多个 XML 文件中"""

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

    def close_spider(self, spider):
        for exporter, xml_file in self.year_to_exporter.values():
            exporter.finish_exporting()
            xml_file.close()

    def _exporter_for_item(self, item):
        adapter = ItemAdapter(item)
        year = adapter["year"]
        if year not in self.year_to_exporter:
            xml_file = open(f"{year}.xml", "wb")
            exporter = XmlItemExporter(xml_file)
            exporter.start_exporting()
            self.year_to_exporter[year] = (exporter, xml_file)
        return self.year_to_exporter[year][0]

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

项目字段的序列化

默认情况下,字段值未经修改地传递给底层序列化库,并且如何序列化它们的决定委托给每个特定的序列化库。

但是,你可以在字段值传递给序列化库之前自定义其序列化方式。

有两种方法可以自定义字段的序列化方式,接下来将进行描述。

1. 在字段中声明序列化器

如果你使用 Item,你可以在字段元数据中声明一个序列化器。序列化器必须是一个可调用对象,它接收一个值并返回其序列化形式。

示例:

import scrapy

def serialize_price(value):
    return f"$ {str(value)}"

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field(serializer=serialize_price)

2. 重写 serialize_field() 方法

你还可以重写 serialize_field() 方法以自定义字段值的导出方式。

确保在你的自定义代码之后调用基类的 serialize_field() 方法。

示例:

from scrapy.exporters import XmlItemExporter

class ProductXmlExporter(XmlItemExporter):
    def serialize_field(self, field, name, value):
        if name == "price":
            return f"$ {str(value)}"
        return super().serialize_field(field, name, value)

内置项目导出器参考

以下是 Scrapy 捆绑的项目导出器列表。其中一些包含输出示例,这些示例假定你正在导出以下两个项目:

Item(name="Color TV", price="1200")
Item(name="DVD player", price="200")

BaseItemExporter

class scrapy.exporters.BaseItemExporter(fields_to_export=None, export_empty_fields=False, encoding='utf-8', indent=0, dont_fail=False)[source]

这是所有项目导出器的(抽象)基类。它支持所有(具体)项目导出器使用的常见功能,例如定义要导出的字段、是否导出空字段或使用哪种编码。

这些功能可以通过 __init__ 方法参数进行配置,这些参数填充各自的实例属性:fields_to_exportexport_empty_fieldsencodingindent

版本 2.0 新增: dont_fail 参数。

export_item(item)[source]

导出给定项目。此方法必须在子类中实现。

serialize_field(field, name, value)[source]

返回给定字段的序列化值。如果你想控制特定字段或值的序列化/导出方式,你可以(在你的自定义项目导出器中)重写此方法。

默认情况下,此方法查找在项目字段中声明的序列化器,并返回将该序列化器应用于值的结果。如果未找到序列化器,则返回未更改的值。

参数:

  • field (Field 对象或 dict 实例) – 正在序列化的字段。如果源项目对象未定义字段元数据,则 field 为空字典。
  • name (str) – 正在序列化的字段名称
  • value – 正在序列化的值
start_exporting()[source]

指示导出过程的开始。一些导出器可能会使用它来生成一些必需的头部(例如,XmlItemExporter)。在导出任何项目之前,你必须调用此方法。

finish_exporting()[source]

指示导出过程的结束。一些导出器可能会使用它来生成一些必需的尾部(例如,XmlItemExporter)。在没有更多项目要导出之后,你必须始终调用此方法。

fields_to_export

要导出的字段、它们的顺序1及其输出名称。

可能的值是:

  • None (所有字段2,默认)
  • 字段列表:
    ['field1', 'field2']
    
  • 键为字段、值为输出名称的字典:
    {'field1': 'Field 1', 'field2': 'Field 2'}
    
export_empty_fields

是否在导出数据中包含空/未填充的项目字段。默认为 False。一些导出器(如 CsvItemExporter)忽略此属性并始终导出所有空字段。

此选项对于字典项目被忽略。

encoding

输出字符编码。

indent

用于在每个级别缩进输出的空格数量。默认为 0

  • indent=None 选择最紧凑的表示,所有项目都在同一行,没有缩进
  • indent<=0 每个项目都在自己的行上,没有缩进
  • indent>0 每个项目都在自己的行上,并按提供的数值缩进

PythonItemExporter

class scrapy.exporters.PythonItemExporter(*, dont_fail: bool = False, **kwargs: Any)[source]

这是项目导出器的基类,它扩展了 BaseItemExporter,并支持嵌套项目。

它将项目序列化为内置 Python 类型,以便任何序列化库(例如 jsonmsgpack)都可以在其之上使用。

XmlItemExporter

class scrapy.exporters.XmlItemExporter(file, item_element='item', root_element='items', **kwargs)[source]

以 XML 格式将项目导出到指定的文件对象。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、io.BytesIO 对象等)
  • root_element (str) – 导出 XML 中的根元素名称。
  • item_element (str) – 导出 XML 中每个项目元素的名称。

__init__ 方法的附加关键字参数将传递给 BaseItemExporter__init__ 方法。

此导出器的典型输出将是:

<?xml version="1.0" encoding="utf-8"?>
<items>
  <item>
    <name>Color TV</name>
    <price>1200</price>
 </item>
  <item>
    <name>DVD player</name>
    <price>200</price>
 </item>
</items>

除非在 serialize_field() 方法中重写,否则多值字段通过将每个值序列化到 <value> 元素中进行导出。这是为了方便,因为多值字段非常常见。

例如,项目:

Item(name=['John', 'Doe'], age='23')

将被序列化为:

<?xml version="1.0" encoding="utf-8"?>
<items>
  <item>
    <name>
      <value>John</value>
      <value>Doe</value>
    </name>
    <age>23</age>
  </item>
</items>

CsvItemExporter

class scrapy.exporters.CsvItemExporter(file, include_headers_line=True, join_multivalued=',', errors=None, **kwargs)[source]

以 CSV 格式将项目导出到给定的文件类对象。如果设置了 fields_to_export 属性,它将用于定义 CSV 列、它们的顺序和它们的列名。export_empty_fields 属性对此导出器没有影响。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、io.BytesIO 对象等)
  • include_headers_line (str) – 如果启用,则使导出器输出一个标题行,其中包含从 BaseItemExporter.fields_to_export 或第一个导出项目字段中获取的字段名称。
  • join_multivalued – 用于连接多值字段(如果找到)的字符(或字符)。
  • errors (str) – 可选字符串,指定如何处理编码和解码错误。有关更多信息,请参阅 io.TextIOWrapper

__init__ 方法的附加关键字参数将传递给 BaseItemExporter__init__ 方法,剩余的参数将传递给 csv.writer() 函数,因此你可以使用任何 csv.writer() 函数参数来自定义此导出器。

此导出器的典型输出将是:

product,price
Color TV,1200
DVD player,200

PickleItemExporter

class scrapy.exporters.PickleItemExporter(file, protocol=0, **kwargs)[source]

以 pickle 格式将项目导出到给定的文件类对象。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、io.BytesIO 对象等)
  • protocol (int) – 要使用的 pickle 协议。

有关更多信息,请参阅 pickle。

__init__ 方法的附加关键字参数将传递给 BaseItemExporter__init__ 方法。

Pickle 不是人类可读的格式,因此不提供输出示例。

PprintItemExporter

class scrapy.exporters.PprintItemExporter(file, **kwargs)[source]

以 pretty print 格式将项目导出到指定的文件对象。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、io.BytesIO 对象等)

__init__ 方法的附加关键字参数将传递给 BaseItemExporter__init__ 方法。

此导出器的典型输出将是:

{'name': 'Color TV', 'price': '1200'}
{'name': 'DVD player', 'price': '200'}

更长的行(如果存在)将进行漂亮格式化。

JsonItemExporter

class scrapy.exporters.JsonItemExporter(file, **kwargs)[source]

以 JSON 格式将项目导出到指定的文件类对象,将所有对象写入为对象列表。附加的 __init__ 方法参数将传递给 BaseItemExporter__init__ 方法,剩余的参数将传递给 JSONEncoder__init__ 方法,因此你可以使用任何 JSONEncoder__init__ 方法参数来自定义此导出器。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、io.BytesIO 对象等)

此导出器的典型输出将是:

[{"name": "Color TV", "price": "1200"},
{"name": "DVD player", "price": "200"}]

警告: JSON 是一种非常简单灵活的序列化格式,但对于大量数据来说,它的扩展性不佳,因为增量(即流模式)解析在 JSON 解析器(任何语言)中都不受支持(如果支持的话),而且大多数解析器只是在内存中解析整个对象。如果你想获得 JSON 的强大功能和简单性以及更适合流的格式,请考虑改用 JsonLinesItemExporter,或将输出分成多个块。

JsonLinesItemExporter

class scrapy.exporters.JsonLinesItemExporter(file, **kwargs)[source]

以 JSON 格式将项目导出到指定的文件类对象,每行写入一个 JSON 编码的项目。附加的 __init__ 方法参数将传递给 BaseItemExporter__init__ 方法,剩余的参数将传递给 JSONEncoder__init__ 方法,因此你可以使用任何 JSONEncoder__init__ 方法参数来自定义此导出器。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、io.BytesIO 对象等)

此导出器的典型输出将是:

{"name": "Color TV", "price": "1200"}
{"name": "DVD player", "price": "200"}

JsonItemExporter 生成的格式不同,此导出器生成的格式非常适合序列化大量数据。

MarshalItemExporter

class scrapy.exporters.MarshalItemExporter(file: BytesIO, **kwargs: Any)[source]

以 Python 特定的二进制格式导出项目(请参阅 marshal)。

参数:

  • file – 用于导出数据的文件类对象。其 write 方法应接受 bytes(以二进制模式打开的磁盘文件、BytesIO 对象等)

并非所有导出器都遵守指定的字段顺序。

使用不公开其所有可能字段的项目对象时,不支持按项目导出不同子集字段的导出器将只导出在第一个导出项目中找到的字段。

Footnotes