Feed导出

在实现爬虫时最常需要的功能之一是能够正确地存储爬取到的数据,通常这意味着生成一个包含爬取到的数据的“导出文件”(通常称为“导出 Feed”),以供其他系统消费。

Scrapy 通过 Feed Exports 开箱即用地提供了此功能,它允许您使用多种序列化格式和存储后端生成包含爬取到的 Item 的 Feed。

本页提供了所有 Feed 导出功能的详细文档。如果您正在寻找分步指南,请查看 Zyte 的导出指南

序列化格式

为了序列化爬取到的数据,Feed Exports 使用 Item 导出器。以下格式开箱即用

但您也可以通过 FEED_EXPORTERS 设置扩展支持的格式。

JSON

JSON lines

CSV

  • FEEDS 设置中的 format 键的值:csv

  • 使用的导出器:CsvItemExporter

  • 要指定要导出的列、它们的顺序及其列名,请使用 FEED_EXPORT_FIELDS。其他 Feed 导出器也可以使用此选项,但这对于 CSV 很重要,因为与许多其他导出格式不同,CSV 使用固定的头部。

XML

Pickle

Marshal

存储后端

在使用 Feed Exports 时,您可以通过 FEEDS 设置定义使用一个或多个 URI 存储 Feed 的位置。Feed Exports 支持多种存储后端类型,这些类型由 URI Scheme 定义。

开箱即用的存储后端包括

如果所需的外部库不可用,某些存储后端可能无法使用。例如,仅当安装了 boto3 库时,S3 后端才可用。

存储 URI 参数

存储 URI 还可以包含在创建 Feed 时会被替换的参数。这些参数包括

  • %(time)s - 在创建 Feed 时被替换为时间戳

  • %(name)s - 被替换为爬虫名称

任何其他命名参数都会被同名的爬虫属性替换。例如,%(site_id)s 在创建 Feed 时会被替换为 spider.site_id 属性。

以下是一些示例说明

  • 使用每个爬虫一个目录的方式存储到 FTP

    • ftp://user:password@ftp.example.com/scraping/feeds/%(name)s/%(time)s.json

  • 使用每个爬虫一个目录的方式存储到 S3

    • s3://mybucket/scraping/feeds/%(name)s/%(time)s.json

注意

爬虫参数成为爬虫属性,因此它们也可以用作存储 URI 参数。

存储后端

本地文件系统

Feed 存储在本地文件系统中。

  • URI Scheme: file

  • 示例 URI: file:///tmp/export.csv

  • 所需外部库:无

请注意,对于本地文件系统存储(仅限),如果您指定绝对路径,例如 /tmp/export.csv,则可以省略 Scheme(仅限 Unix 系统)。或者,您也可以使用 pathlib.Path 对象。

FTP

Feed 存储在 FTP 服务器中。

  • URI Scheme: ftp

  • 示例 URI: ftp://user:pass@ftp.example.com/path/to/export.csv

  • 所需外部库:无

FTP 支持两种不同的连接模式:主动或被动。Scrapy 默认使用被动连接模式。要改用主动连接模式,请将 FEED_STORAGE_FTP_ACTIVE 设置为 True

对于此存储后端,FEEDS 设置中 overwrite 键的默认值为:True

注意

overwrite 中的 True 值将导致您丢失之前版本的数据。

此存储后端使用 延迟文件传输

S3

Feed 存储在 Amazon S3 上。

  • URI Scheme: s3

  • 示例 URI

    • s3://mybucket/path/to/export.csv

    • s3://aws_key:aws_secret@mybucket/path/to/export.csv

  • 所需外部库:boto3 >= 1.20.0

AWS 凭据可以通过 URI 中的用户/密码形式传递,也可以通过以下设置传递

您还可以使用以下设置定义导出 Feed 的自定义 ACL、自定义 endpoint 和区域名称

对于此存储后端,FEEDS 设置中 overwrite 键的默认值为:True

注意

overwrite 中的 True 值将导致您丢失之前版本的数据。

此存储后端使用 延迟文件传输

Google Cloud Storage (GCS)

在 2.3 版本中添加。

Feed 存储在 Google Cloud Storage 上。

  • URI Scheme: gs

  • 示例 URI

    • gs://mybucket/path/to/export.csv

  • 所需外部库:google-cloud-storage

有关身份验证的更多信息,请参阅 Google Cloud 文档

您可以通过以下设置设置 项目 ID访问控制列表 (ACL)

对于此存储后端,FEEDS 设置中 overwrite 键的默认值为:True

注意

overwrite 中的 True 值将导致您丢失之前版本的数据。

此存储后端使用 延迟文件传输

标准输出

Feed 被写入 Scrapy 进程的标准输出。

  • URI Scheme: stdout

  • 示例 URI: stdout:

  • 所需外部库:无

延迟文件传输

如上所述,某些描述的存储后端使用延迟文件传输。

这些存储后端不会在 Item 被爬取时立即将它们上传到 Feed URI。而是,Scrapy 将 Item 写入一个临时本地文件,只有当所有文件内容都写入完毕后(即在爬取结束时),才会将该文件上传到 Feed URI。

如果您在使用这些存储后端之一时希望 Item 传输更快开始,请使用 FEED_EXPORT_BATCH_ITEM_COUNT 将输出 Item 分割到多个文件中,每个文件包含指定的 Item 最大数量。这样,一旦文件达到最大 Item 数量,该文件就会被传输到 Feed URI,从而使 Item 传输在爬取结束之前很久就开始。

Item 过滤

在 2.6.0 版本中添加。

您可以使用 Feed 选项 中的 item_classes 选项过滤您想允许导出到特定 Feed 的 Item。只有指定类型的 Item 会被添加到 Feed 中。

item_filter 选项由 ItemFilter 类实现,它是 Feed 选项 item_filter 的默认值。

您可以通过实现 ItemFilter 类的方法 accepts 并以 feed_options 作为参数来创建自己的自定义过滤类。

例如

class MyCustomFilter:
    def __init__(self, feed_options):
        self.feed_options = feed_options

    def accepts(self, item):
        if "field1" in item and item["field1"] == "expected_data":
            return True
        return False

您可以将您的自定义过滤类分配给 Feed 的 item_filter 选项。有关示例,请参阅 FEEDS

ItemFilter

class scrapy.extensions.feedexport.ItemFilter(feed_options: dict[str, Any] | None)[source]

FeedExporter 将使用此方法来决定是否应将 Item 导出到特定 Feed。

参数:

feed_options (dict) – FeedExporter 传递的 Feed 特定选项

accepts(item: Any) bool[source]

如果应导出 item 则返回 True,否则返回 False

参数:

item (Scrapy items) – 用户想要检查是否可接受的爬取到的 Item

返回:

True 如果被接受,False 否则

返回类型:

bool

后处理

在 2.6.0 版本中添加。

Scrapy 提供一个选项来激活插件,在将 Feed 导出到 Feed 存储之前对其进行后处理。除了使用 内置插件,您还可以创建自己的 插件

这些插件可以通过 Feed 的 postprocessing 选项激活。此选项必须传递一个后处理插件列表,顺序即为您希望处理 Feed 的顺序。这些插件可以声明为导入字符串,也可以是插件的导入类。插件的参数可以通过 Feed 选项传递。有关示例,请参阅 Feed 选项

内置插件

class scrapy.extensions.postprocessing.GzipPlugin(file: BinaryIO, feed_options: dict[str, Any])[source]

使用 gzip 压缩接收到的数据。

接受的 feed_options 参数

  • gzip_compresslevel

  • gzip_mtime

  • gzip_filename

有关参数的更多信息,请参阅 gzip.GzipFile

class scrapy.extensions.postprocessing.LZMAPlugin(file: BinaryIO, feed_options: dict[str, Any])[source]

使用 lzma 压缩接收到的数据。

接受的 feed_options 参数

  • lzma_format

  • lzma_check

  • lzma_preset

  • lzma_filters

注意

在 pypy 版本 7.3.1 及更旧版本中不能使用 lzma_filters

有关参数的更多信息,请参阅 lzma.LZMAFile

class scrapy.extensions.postprocessing.Bz2Plugin(file: BinaryIO, feed_options: dict[str, Any])[source]

使用 bz2 压缩接收到的数据。

接受的 feed_options 参数

  • bz2_compresslevel

有关参数的更多信息,请参阅 bz2.BZ2File

自定义插件

每个插件都是一个类,必须实现以下方法

__init__(self, file, feed_options)

初始化插件。

参数:
  • file – 文件类对象,至少实现了 writetellclose 方法

  • feed_options (dict) – Feed 特定 选项

write(self, data)

处理并写入 data (bytesmemoryview) 到插件的目标文件中。它必须返回写入的字节数。

close(self)

清理插件。

例如,您可能需要关闭一个文件包装器,该包装器可能用于压缩写入 __init__ 方法中接收到的文件的数据。

警告

不要从 __init__ 方法中关闭文件。

要将参数传递给您的插件,请使用 Feed 选项。然后您可以从您的插件的 __init__ 方法中访问这些参数。

设置

以下是用于配置 Feed Exports 的设置

FEEDS

在 2.1 版本中添加。

默认值: {}

一个字典,其中每个键都是一个 Feed URI(或 pathlib.Path 对象),每个值都是一个嵌套字典,包含特定 Feed 的配置参数。

此设置是启用 Feed 导出功能所必需的。

有关支持的 URI Scheme,请参阅 存储后端

例如

{
    'items.json': {
        'format': 'json',
        'encoding': 'utf8',
        'store_empty': False,
        'item_classes': [MyItemClass1, 'myproject.items.MyItemClass2'],
        'fields': None,
        'indent': 4,
        'item_export_kwargs': {
           'export_empty_fields': True,
        },
    },
    '/home/user/documents/items.xml': {
        'format': 'xml',
        'fields': ['name', 'price'],
        'item_filter': MyCustomFilter1,
        'encoding': 'latin1',
        'indent': 8,
    },
    pathlib.Path('items.csv.gz'): {
        'format': 'csv',
        'fields': ['price', 'name'],
        'item_filter': 'myproject.filters.MyCustomFilter2',
        'postprocessing': [MyPlugin1, 'scrapy.extensions.postprocessing.GzipPlugin'],
        'gzip_compresslevel': 5,
    },
}

以下是接受的键列表以及在特定 Feed 定义未提供该键时用作回退值的设置

FEED_EXPORT_ENCODING

默认值:"utf-8"回退None

用于导出 feed 的编码。

如果设置为 None,除了JSON输出外,所有都使用UTF-8。JSON输出出于历史原因使用安全的数字编码(\uXXXX序列)。

如果您想JSON也使用UTF-8,请使用 "utf-8"

在 2.8 版本中有所变动: startproject 命令现在会在生成的 settings.py 文件中将此设置设为 "utf-8"

FEED_EXPORT_FIELDS

默认值:None

使用 FEED_EXPORT_FIELDS 设置定义要导出的字段、它们的顺序和输出名称。有关更多信息,请参阅 BaseItemExporter.fields_to_export

FEED_EXPORT_INDENT

默认值:0

在每个层级缩进输出的空格数。如果 FEED_EXPORT_INDENT 是一个非负整数,则数组元素和对象成员将以该缩进级别进行美观打印。缩进级别为 0(默认值)或负数,会将每个项目放在新行上。None 选择最紧凑的表示形式。

目前仅由 JsonItemExporterXmlItemExporter 实现,即当您导出到 .json.xml 时。

FEED_STORE_EMPTY

默认值:True

是否导出空 feed(即不包含任何 item 的 feed)。如果设置为 False,并且没有 item 可导出,则不会创建新文件,也不会修改现有文件,即使覆盖 feed 选项已启用。

FEED_STORAGES

默认值: {}

一个字典,包含您的项目支持的其他 feed 存储后端。键是 URI 方案,值是存储类的路径。

FEED_STORAGE_FTP_ACTIVE

默认值:False

导出 feed 到 FTP 服务器时,是使用主动连接模式(True)还是使用被动连接模式(False,默认值)。

有关 FTP 连接模式的信息,请参阅主动和被动FTP有什么区别?

FEED_STORAGE_S3_ACL

默认值:''(空字符串)

一个字符串,包含您的项目导出到 Amazon S3 的 feed 的自定义 ACL。

有关可用值的完整列表,请访问 Amazon S3 文档中的预设 ACL 部分。

FEED_STORAGES_BASE

默认

{
    "": "scrapy.extensions.feedexport.FileFeedStorage",
    "file": "scrapy.extensions.feedexport.FileFeedStorage",
    "stdout": "scrapy.extensions.feedexport.StdoutFeedStorage",
    "s3": "scrapy.extensions.feedexport.S3FeedStorage",
    "ftp": "scrapy.extensions.feedexport.FTPFeedStorage",
}

一个字典,包含 Scrapy 支持的内置 feed 存储后端。您可以通过在 FEED_STORAGES 中将它们的 URI 方案赋值为 None 来禁用它们中的任何一个。例如,要在不替换的情况下禁用内置的 FTP 存储后端,请将其放在您的 settings.py 文件中

FEED_STORAGES = {
    "ftp": None,
}

FEED_EXPORTERS

默认值: {}

一个字典,包含您的项目支持的其他导出器。键是序列化格式,值是 Item exporter 类的路径。

FEED_EXPORTERS_BASE

默认

{
    "json": "scrapy.exporters.JsonItemExporter",
    "jsonlines": "scrapy.exporters.JsonLinesItemExporter",
    "jsonl": "scrapy.exporters.JsonLinesItemExporter",
    "jl": "scrapy.exporters.JsonLinesItemExporter",
    "csv": "scrapy.exporters.CsvItemExporter",
    "xml": "scrapy.exporters.XmlItemExporter",
    "marshal": "scrapy.exporters.MarshalItemExporter",
    "pickle": "scrapy.exporters.PickleItemExporter",
}

一个字典,包含 Scrapy 支持的内置 feed 导出器。您可以通过在 FEED_EXPORTERS 中将它们的序列化格式赋值为 None 来禁用它们中的任何一个。例如,要在不替换的情况下禁用内置的 CSV 导出器,请将其放在您的 settings.py 文件中

FEED_EXPORTERS = {
    "csv": None,
}

FEED_EXPORT_BATCH_ITEM_COUNT

在 2.3.0 版本中添加。

默认值:0

如果指定一个大于 0 的整数,Scrapy 将生成多个输出文件,每个文件最多存储指定数量的 item。

生成多个输出文件时,必须在 feed URI 中使用至少一个以下占位符来指示如何生成不同的输出文件名

  • %(batch_time)s - 在创建 feed 时替换为时间戳(例如 2020-03-28T14-45-08.237134

  • %(batch_id)d - 替换为批次的基于 1 的序号。

    使用printf 风格的字符串格式化来改变数字格式。例如,通过根据需要添加前导零,将批次 ID 变为 5 位数字,使用 %(batch_id)05d(例如,3 变为 00003123 变为 00123)。

例如,如果您的设置包含

FEED_EXPORT_BATCH_ITEM_COUNT = 100

并且您的 crawl 命令行是

scrapy crawl spidername -o "dirname/%(batch_id)d-filename%(batch_time)s.json"

上面的命令行可以生成一个目录树,如下所示

->projectname
-->dirname
--->1-filename2020-03-28T14-45-08.237134.json
--->2-filename2020-03-28T14-45-09.148903.json
--->3-filename2020-03-28T14-45-10.046092.json

其中第一个和第二个文件都包含恰好 100 个 item。最后一个文件包含 100 个或更少的 item。

FEED_URI_PARAMS

默认值:None

一个字符串,包含一个函数的导入路径,用于设置使用printf 风格的字符串格式化应用于 feed URI 的参数。

函数签名应如下所示

scrapy.extensions.feedexport.uri_params(params, spider)

返回一个dict,包含要使用printf 风格的字符串格式化应用于 feed URI 的键值对。

参数:

注意

函数应该返回一个新字典,原地修改接收到的 params 已被弃用。

例如,要在 feed URI 中包含源爬虫的 name

  1. 在您的项目中的某个位置定义以下函数

    # myproject/utils.py
    def uri_params(params, spider):
        return {**params, "spider_name": spider.name}
    
  2. 在您的设置中将 FEED_URI_PARAMS 指向该函数

    # myproject/settings.py
    FEED_URI_PARAMS = "myproject.utils.uri_params"
    
  3. 在您的 feed URI 中使用 %(spider_name)s

    scrapy crawl <spider_name> -o "%(spider_name)s.jsonl"