十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Scrapy Feed Exports 完整指南:FEEDS 配置、存储后端、URI 参数、批量导出与后处理插件

Scrapy Feed Exports 完整指南:FEEDS 配置、存储后端、URI 参数、批量导出与后处理插件 Scrapy Feed Exports 完整指南FEEDS 配置、存储后端、URI 参数、批量导出与后处理插件【免费下载链接】scrapyScrapy, a fast high-level web crawling scraping framework for Python.项目地址: https://gitcode.com/GitHub_Trending/sc/scrapy本篇技术指南以 Scrapy 官方文档中的 Feed Exports数据导出章节为核心系统讲解如何在 Scrapy 项目中将抓取结果导出为 JSON、JSON Lines、CSV、XML 等格式的文件并投递到本地文件系统、FTP、S3、GCS 等存储后端。读完后你可以独立完成FEEDS配置、批量分片导出FEED_EXPORT_BATCH_ITEM_COUNT、URI 占位符模板、按 Item 类型过滤导出以及编写自定义过滤器和压缩后处理插件并理解其底层在 scrapy/extensions/feedexport.py 中的实现机制。一、Feed Exports 是什么如何被触发实现爬虫时最常需要的能力之一是把抓取到的数据妥善存储——通常意味着生成一个包含抓取数据的“导出文件”export feed供其他系统消费。Scrapy 通过Feed Exports 扩展原生提供了这一能力支持多种序列化格式与多种存储后端。从源码结构看整个功能由 scrapy/extensions/feedexport.py 中的FeedExporter类实现它在from_crawler中挂载三个信号crawler.signals.connect(exporter.open_spider, signals.spider_opened) crawler.signals.connect(exporter.close_spider, signals.spider_closed) crawler.signals.connect(exporter.item_scraped, signals.item_scraped)spider_opened时为FEEDS中声明的每个 feed URI 创建一个FeedSlot每个 slot 持有 storage、exporter、filter、itemcount 等状态item_scraped时每个 slot 先用filter.accepts(item)判断该 item 是否可导出接受则调用slot.exporter.export_item(item)序列化spider_closed时逐个执行finish_exporting()并调用storage.store(file)完成落盘或上传同时上报feedexport/success_count/storage或feedexport/failed_count/storage统计发送feed_slot_closed/feed_exporter_closed信号。启用该功能的开关是FEEDS设置FEED_URI/FEED_FORMAT已被弃用仅保留向后兼容并会发出ScrapyDeprecationWarning。此外命令行scrapy crawl spider -o URI也会走 feed exportsscrapy/utils/conf.py 中的feed_process_params_from_cli会把-o/-O参数解析成FEEDS结构——格式可写在 URI 末尾冒号之后如out.json:json或-表示stdout:也可以直接用文件扩展名推断.json→json且settings.py中已声明的FEEDS条目优先于命令行同名 URI。-O/--overwrite-output则等价于-o加上overwrite: True。二、序列化格式Serialization formatsfeed exports 复用 Item exporters实现位于 scrapy/exporters.py来序列化抓取数据。开箱即用的格式与对应导出器FEEDS中format的取值使用的 Exporter说明jsonscrapy.exporters.JsonItemExporter整个 feed 是一个 JSON 数组大数据量下建议改用 jsonlinesJSON 需整体缓冲存在大内存占用风险jsonlines/jsonl/jlscrapy.exporters.JsonLinesItemExporter每行一个 JSON 对象流式写出适合大 feedcsvscrapy.exporters.CsvItemExporter固定表头用FEED_EXPORT_FIELDS指定列名、顺序与输出列名xmlscrapy.exporters.XmlItemExporter生成 XML 文档picklescrapy.exporters.PickleItemExporterPython 序列化供 Python 程序消费marshalscrapy.exporters.MarshalItemExporterPythonmarshal序列化以上别名定义在 scrapy/settings/default_settings.py 的FEED_EXPORTERS_BASE中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, }可以通过FEED_EXPORTERS设置扩展自定义格式值为 Item exporter 类的导入路径也可以在FEED_EXPORTERS中给某个格式赋None来禁用内置导出器例如禁用 CSV# settings.py FEED_EXPORTERS { csv: None, }各格式的关键行为在 scrapy/exporters.py 的BaseItemExporter._configure中统一处理它从 kwargs 中弹出encoding、fields_to_export、export_empty_fields、indent四个选项get_serialized_fields负责按fields_to_export决定导出哪些字段、顺序如何并对字段值应用字段元数据中声明的serializer。三、FEEDS 设置核心配置详解FEEDS默认{}是启用 feed 导出的必选设置。它是一个字典键是 feed URI或pathlib.Path对象值是该 feed 的嵌套配置字典。官方文档给出的完整示例{ 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 定义可接受的键以及未显式给出时的回退设置回退逻辑实现在 scrapy/utils/conf.py 的feed_complete_default_values_from_settings键含义回退设置format序列化格式必填无回退值无batch_item_count每个输出文件的最大 item 数FEED_EXPORT_BATCH_ITEM_COUNTencoding文件编码FEED_EXPORT_ENCODINGfields要导出的字段、顺序与输出名FEED_EXPORT_FIELDSitem_classes允许导出的 Item 类列表未定义或为空则导出全部 item无item_filter自定义 item 过滤器类默认是scrapy.extensions.feedexport.ItemFilter无indentJSON/XML 缩进FEED_EXPORT_INDENTitem_export_kwargs传给对应 item exporter 构造函数的关键字参数字典无默认{}overwrite文件已存在时覆盖True还是追加False取决于存储后端见下表store_empty无 item 时是否仍导出空 feedFEED_STORE_EMPTYuri_params自定义 URI 参数函数的导入路径FEED_URI_PARAMSpostprocessing后处理插件列表按列表顺序依次处理无各存储后端的overwrite默认值本地文件系统FalseFTP / FTPSTrue注意部分 FTP 服务器不支持APPE追加命令S3True不支持追加GCSTrue不支持追加stdoutFalse不支持覆盖overwrite: True会丢失旧版本数据需要特别留意。四、存储后端Storages通过FEEDS中的 URI 定义 feed 存到哪里。URI 的scheme 决定使用哪个存储后端内置后端列表本地文件系统、FTP、FTPS、S3需要s3extra、GCS需要gcsextra、标准输出。完整的 scheme 到类名映射在 scrapy/settings/default_settings.py 的FEED_STORAGES_BASE中FEED_STORAGES_BASE { : scrapy.extensions.feedexport.FileFeedStorage, file: scrapy.extensions.feedexport.FileFeedStorage, ftp: scrapy.extensions.feedexport.FTPFeedStorage, ftps: scrapy.extensions.feedexport.FTPFeedStorage, gs: scrapy.extensions.feedexport.GCSFeedStorage, s3: scrapy.extensions.feedexport.S3FeedStorage, stdout: scrapy.extensions.feedexport.StdoutFeedStorage, }本地文件系统file示例 URIfile:///tmp/export.csv外部依赖无唯一可以省略 scheme 的后端直接写路径如/tmp/export.csv即可也可以直接使用pathlib.Path对象作为FEEDS的键。源码中FileFeedStorage按overwrite选项选择wb覆盖或ab追加打开文件并在打开时自动创建缺失的父目录见 scrapy/extensions/feedexport.py。FTP / FTPS示例 URIftp://user:passftp.example.com/path/to/export.csvFTPS 为ftps://user:passftp.example.com/path/to/export.csv2.18.0 新增走 TLS 且校验服务器证书外部依赖无凭据与数据在 FTP 中是明文传输尽可能改用 FTPS默认使用被动模式passive改为主动模式需设置FEED_STORAGE_FTP_ACTIVE True默认Falseoverwrite默认True且该后端使用延迟文件投递见下文从源码看FTPFeedStorage解析 URI 时对密码做了unquote并按 scheme 是否为ftps决定是否启用 TLS最终调用 scrapy/utils/ftp.py 的ftp_store_file完成上传。注意SFTP基于 SSH 的无关协议不在内置后端之列需使用第三方插件项目。Amazon S3s3示例 URIs3://mybucket/path/to/export.csv或s3://aws_key:aws_secretmybucket/path/to/export.csv需要安装s3extra凭据可以写在 URI 的用户名/密码里也可以通过以下设置提供AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKEN临时安全凭据才需要可通过设置定制FEED_STORAGE_S3_ACL自定义 ACL默认空字符串、AWS_ENDPOINT_URL自定义 endpoint、AWS_REGION_NAME、AWS_MAX_POOL_CONNECTIONSoverwrite默认True不支持追加使用延迟文件投递S3FeedStorage的from_crawler会把上述设置逐项注入boto3客户端见 scrapy/extensions/feedexport.py缺省未安装boto3时抛出NotConfigured(missing boto3 library)。Google Cloud Storagegs示例 URIgs://mybucket/path/to/export.csv需要安装gcsextra可通过FEED_STORAGE_GCS_ACLACL和GCS_PROJECT_ID项目 ID设置配置认证方式参见 Google Cloud 官方文档overwrite默认True不支持追加使用延迟文件投递标准输出stdout示例 URIstdout:命令行-o -也等价于它外部依赖无直接把 feed 写到 Scrapy 进程的标准输出不支持覆盖显式overwrite: True会触发告警日志延迟文件投递Delayed file deliveryFTP、FTPS、S3、GCS 这些后端不会把 item 边抓取边上传。它们的共同基类BlockingFeedStorage在open阶段返回一个NamedTemporaryFile可指定临时目录FEED_TEMPDIR默认None即系统临时目录先把全部 item 写入本地临时文件等整个 crawl 结束、文件内容全部写完后才上传到 feed URI。如果希望数据更早开始投递使用FEED_EXPORT_BATCH_ITEM_COUNT把输出切成多个文件某个文件一旦达到上限 item 数就立即交付到 feed URI不必等 crawl 结束见下文批量导出一节。自定义与禁用后端通过FEED_STORAGES默认{}注册自定义存储后端键是 URI scheme值是你的 storage 类路径。自定义类需遵循FeedStorageProtocol约定的接口__init__(uri, *, feed_optionsNone)、open(spider)返回文件对象、store(file)保存文件流。通过在FEED_STORAGES中给某个 scheme 赋None可禁用内置后端例如禁用 FTP# settings.py FEED_STORAGES { ftp: None, }FeedExporter._load_components用settings.getwithbase(...)合并用户设置与*_BASE基础字典再过滤掉None值without_none_values因此None即“从可用 scheme 中移除”。五、存储 URI 参数占位符模板URI 中可以包含 printf 风格的占位符在 feed 创建时被替换%(time)s—— feed 创建时的时间戳ISO 格式:替换为-微秒置 0%(name)s—— spider 名称任何其他命名参数都会替换为同名的 spider 属性。例如%(site_id)s会被spider.site_id属性替换示例每个 spider 一个目录FTPftp://user:passwordftp.example.com/scraping/feeds/%(name)s/%(time)s.jsonS3s3://mybucket/scraping/feeds/%(name)s/%(time)s.json两个实用推论Spider arguments 也能作为 URI 参数。scrapy crawl spider -a keyvalue传入的 spider 参数会成为 spider 属性因此%(key)s同样可以被替换。百分号编码不会被误解析。只有%(...)s形式的占位符被替换其余百分号原样保留——URI 里的%20空格或百分号编码的 FTP 凭据都能正常工作。源码层面scrapy/extensions/feedexport.py 用正则_FEED_URI_PLACEHOLDER_RE先精确匹配占位符把其余%转义为%%后再执行% uri_params格式化保证了这一语义。参数如何生成可以看FeedExporter._get_uri_params默认参数包含 spider 的所有属性、time、batch_time带微秒的 UTC 时间戳和batch_id当前批次号无批处理时为 1随后交给可选的uri_params函数加工。六、FEED_URI_PARAMS自定义 URI 参数函数FEED_URI_PARAMS默认None是一个函数的导入路径字符串用于在 feed URI 上应用额外的 printf 风格格式化参数。函数签名def uri_params(params, spider): 返回要应用到 feed URI 的键值对字典。 :param params: 默认的键值对包括 batch_id、batch_time、time :param spider: 产生 feed item 的源 spider return {**params, spider_name: spider.name}params[batch_id]文件批次 ID见FEED_EXPORT_BATCH_ITEM_COUNT当该设置为0时恒为1params[batch_time]UTC 日期时间ISO 格式且:替换为-params[time]batch_time的微秒置0版本注意必须返回新字典而不能就地修改传入的params。完整使用步骤把 spider 名称放进 feed URI# myproject/utils.py def uri_params(params, spider): return {**params, spider_name: spider.name}# myproject/settings.py FEED_URI_PARAMS myproject.utils.uri_params然后就可以在 URI 中用%(spider_name)sscrapy crawl spider_name -o %(spider_name)s.jsonl七、批量导出FEED_EXPORT_BATCH_ITEM_COUNT默认0不分批。赋一个大于0的整数后Scrapy 会生成多个输出文件每个最多存指定数量的 item。生成多个文件时feed URI 必须至少包含一个占位符用于区分不同输出文件名%(batch_time)s—— feed 创建时的时间戳例如2020-03-28T14-45-08.237134%(batch_id)d—— 批次的 1 起始序号可用 printf 修饰符调整数字格式例如%(batch_id)05d会补前导零3→00003123→00123示例settings 中FEED_EXPORT_BATCH_ITEM_COUNT 100命令行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_scraped中每次slot.itemcount达到batch_item_count就关闭旧 slot触发storage.store并_start_new_batch(batch_id 1)用模板重新渲染出新 URI见 scrapy/extensions/feedexport.py。FeedExporter._settings_are_valid会校验启用批量导出但 URI 中不含%(batch_time)s或%(batch_id)...时输出错误日志并拒绝启用该功能。该机制同样服务于“延迟投递”后端批量能把数据交付时间从 crawl 结束提前到第一个批次写满时。相关行为在 tests/test_feedexport.py 与 tests/test_feedexport_batch.py 中有覆盖。八、Item 过滤Item filtering通过item_classes选项可以过滤某个 feed 允许写入的 item 类型——只有指定类型的 item 会被加入该 feed未定义或为空则导出全部 item。item_classes由scrapy.extensions.feedexport.ItemFilter类实现它也是item_filterfeed 选项的默认值。其核心逻辑见 scrapy/extensions/feedexport.pydef accepts(self, item): if self.item_classes: return isinstance(item, self.item_classes) return True # accept all items by default也可以编写自定义过滤类实现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选项可直接传类也可传导入路径字符串如myproject.filters.MyCustomFilter2二者在官方FEEDS示例中都有演示。九、后处理插件Post-processingScrapy 提供postprocessing选项允许在数据写入存储前通过插件链处理 feed如压缩。插件以列表形式按顺序声明可写导入字符串或直接写已导入的类插件所需参数通过该 feed 的 feed options 传入。内置插件实现位于 scrapy/extensions/postprocessing.pyGzipPluginscrapy.extensions.postprocessing.GzipPlugingzip 压缩。可选项gzip_compresslevel默认 9、gzip_mtime、gzip_filename参数语义与gzip.GzipFile一致Bz2Pluginscrapy.extensions.postprocessing.Bz2Pluginbzip2 压缩。可选项bz2_compresslevel默认 9LZMAPluginscrapy.extensions.postprocessing.LZMAPluginLZMA 压缩。可选项lzma_format、lzma_check默认 -1、lzma_preset、lzma_filters注意旧版 PyPy 7.3.1 及以下不支持lzma_filters配合示例来自官方FEEDS示例postprocessing: [MyPlugin1, scrapy.extensions.postprocessing.GzipPlugin]加gzip_compresslevel: 5即可得到 CSV 导出后经自定义插件处理再 gzip 压缩的items.csv.gz。自定义插件接口每个插件是一个类必须实现以下方法__init__(self, file, feed_options)—— 初始化插件。file是至少实现了write、tell、close的文件类对象feed_options是该 feed 的选项字典可从中读取插件参数write(self, data)—— 处理并写入databytes或memoryview到目标文件返回写入的字节数close(self)—— 清理资源例如关闭你在__init__中创建的压缩文件包装器。不要在__init__中关闭传入的file链式执行由PostProcessingManager完成它继承io.IOBase因此可以像文件一样被 exporter 使用例如CsvItemExporter会用io.TextIOWrapper包装它内部按声明顺序的逆序把插件层层套在目标文件之上write从“链头”插件开始逐层传递close同样从链头逐级关闭。十、全部设置一览Feed Exports 涉及的全部设置默认值取自 scrapy/settings/default_settings.py设置默认值说明FEEDS{}必选。feed URI → 配置字典启用 feed 导出的入口FEED_EXPORT_ENCODINGNonefeed 编码。None时除 JSON 外均按 UTF-8 处理JSON 出于历史原因使用\uXXXX安全数字编码想让 JSON 也输出 UTF-8请显式设为utf-8FEED_STORE_EMPTYTrue是否导出空 feed。False时无 item 则不创建文件、不修改已有文件即使overwrite启用FEED_EXPORT_FIELDSNone定义导出字段、顺序与输出名语义同BaseItemExporter.fields_to_exportFEED_EXPORT_INDENT0每层缩进空格数。非负整数时 JSON/XML 数组元素与成员按该缩进美化输出0或负数时每个 item 独占一行None为最紧凑表示。目前仅JsonItemExporter与XmlItemExporter实现FEED_STORAGES{}注册自定义存储后端scheme → 类路径赋None可禁用内置后端FEED_STORAGE_FTP_ACTIVEFalseFTP 是否用主动模式True还是被动模式False默认FEED_STORAGE_S3_ACL导出到 Amazon S3 的自定义 ACLFEED_EXPORTERS{}注册自定义导出器format → 类路径赋None可禁用内置导出器FEED_EXPORT_BATCH_ITEM_COUNT0大于 0 时每个输出文件最多存多少个 item需配合 URI 占位符FEED_STORAGES_BASE见上文 7 个内置 scheme内置存储后端字典FEED_EXPORTERS_BASE见上文 8 个内置 format内置导出器字典FEED_URI_PARAMSNone自定义 URI 参数函数的导入路径FEED_TEMPDIRNone延迟投递后端写临时文件的目录非None时必须是已存在目录关于FEED_EXPORT_ENCODING的一个源码细节JsonLinesItemExporter的构造里执行self._kwargs.setdefault(ensure_ascii, not self.encoding)——即编码为None时ensure_asciiTrue这就是“JSON 在默认编码下使用\uXXXX转义”这一历史行为的来源见 scrapy/exporters.py。十一、延伸阅读与验证路径文档docs/topics/feed-exports.rst、Item exporters 文档、settings 参考核心实现scrapy/extensions/feedexport.pyFeedExporter、FeedSlot、各 storage、ItemFilter、scrapy/extensions/postprocessing.pyGzip/Bz2/LZMA 插件与PostProcessingManager、scrapy/exporters.py、scrapy/utils/conf.pyfeed_complete_default_values_from_settings、feed_process_params_from_cli测试参考tests/test_feedexport.py、tests/test_feedexport_batch.py、tests/test_feedexport_storages.py、tests/test_feedexport_uri_params.py、tests/test_feedexport_postprocess.py样例数据见 tests/sample_data/feeds/命令行用法-o/-Odocs/topics/commands.rst适用前提以上行为以当前仓库Scrapy 主干为准FTPS 后端自 2.18.0 引入FEED_URI/FEED_FORMAT为弃用兼容路径新配置一律使用FEEDS。S3/GCS 后端分别依赖s3、gcsextra未安装对应依赖时相关 scheme 会被判定为不可用并记录错误日志。【免费下载链接】scrapyScrapy, a fast high-level web crawling scraping framework for Python.项目地址: https://gitcode.com/GitHub_Trending/sc/scrapy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表