首页
/ Scrapy 版本管理与 API 稳定性:A.B.C 语义、弃用策略与源码级实现解读

Scrapy 版本管理与 API 稳定性:A.B.C 语义、弃用策略与源码级实现解读

2026-09-04 13:06:24作者:谭伦延

本文基于 Scrapy 仓库中的 版本管理文档,系统讲解 Scrapy 的版本号构成规则(A.B.C 三段式)、开发版本的命名约定、API 稳定性承诺以及"至少一年"的弃用策略。同时结合当前仓库源码(版本号加载机制、ScrapyDeprecationWarning 警告体系、create_deprecated_class 弃用类工厂等)与 发布说明,帮助你准确判断何时升级 Scrapy、升级前需要检查哪些弃用项,以及为什么以单下划线开头的成员不应被依赖。

版本号结构:A.B.C 三段式

Scrapy 的版本号由三个数字组成,写作 A.B.C,每一段的含义如下:

名称 含义
A 主版本号(major) 很少变化,代表非常重大的改动
B 发布号(release) 包含大量改动,包括新功能,也可能包含破坏向后兼容性的变更,但官方力求将此类情况降到最低
C 修复号(bugfix) 仅包含 bug 修复

原文档给出的例子是:1.1.11.1 系列的第一个 bugfix 发布版本(可直接用于生产环境)。

非兼容性变更必须写进发布说明

一个关键承诺是:所有破坏向后兼容性的变更都会在 发布说明(release notes) 中被明确提及,可能需要在升级前给予特别关注。这一点在当前仓库中可以直接验证:docs/news.rst 的每个版本条目下都固定设有 Backward-incompatible changesDeprecationsDeprecation removals 三个小节。例如最新版本 Scrapy 2.18.0 的条目就明确列出了"移除 zope.interface 运行时接口标记"、"Crawler.stats 等属性在爬取开始前的行为从返回 None 变为抛出 RuntimeError"等破坏性变更;而更旧的版本条目中则能看到 "Removed the --output-format/-t command line option, deprecated in ..." 这类"弃用一年后移除"的记录——这正是下节弃用策略在实践中的体现。

开发版本不遵循三段式

开发版(development release)不使用三段式版本号,而是以 dev 作为后缀发布,例如 1.3dev。原文档还附有一条重要的历史注记:

在 Scrapy 0.* 系列中,Scrapy 曾使用奇数版本号表示开发版(如 0.13 之后的下一个开发版用 0.15 表示)。自 Scrapy 1.0 起不再沿用这一惯例。

与之配套的另一条约定是:从 Scrapy 1.0 开始,所有发布版都应被视为生产就绪(production-ready),不存在"奇数版不稳定"的说法。

当前仓库的版本号是如何实现的

结合源码可以看到,版本号的定义与读取链路非常简洁:

  1. 版本号本身集中存储在 scrapy/VERSION 文件中,当前仓库中的内容为 2.18.0
  2. 包初始化文件 scrapy/init.py 在导入时读取该文件并解析:
__version__ = (pkgutil.get_data(__package__, "VERSION") or b"").decode("ascii").strip()
version_info = tuple(int(v) if v.isdigit() else v for v in __version__.split("."))

version_info 的解析方式值得注意:它把能转成整数的段转成 int,其余部分(比如开发版的 1.3dev 这类非数字段)原样保留为字符串,因此 dev 后缀版本也能被正确解析。

这也解释了前文"开发版不遵循三段式"的说法:VERSION 文件是纯文本,既可以写 2.18.0,也可以写 2.19dev,加载逻辑对两者都兼容。命令行中可通过 scrapy version 命令(对应 scrapy/commands/version.py)随时查看当前安装环境的版本号。

API 稳定性:1.0 之后的核心承诺

API 稳定性(API stability)是 Scrapy 1.0 发布的主要目标之一。原文档给出两条判断准则:

  1. 单下划线前缀 = 私有。以单个下划线 _ 开头的函数或方法被视为私有实现,不应依赖其长期稳定。例如 scrapy/core/downloader/ 下的 _base_http.py_base_streaming.py 等模块名,以及 scrapy/utils/deprecate.py 中的 _clspath() 这类辅助函数,都属于此类——它们随时可能在下一个发布版中被重命名或删除。
  2. "稳定"不等于"完整"。稳定的 API 可以随版本增长出新的方法或功能,但已存在的成员应保持行为不变。换言之,Scrapy 承诺的是"不悄悄破坏",而不是"不扩展"。

弃用策略:至少一年的过渡期

原文档的弃用政策(Deprecation policy)可以概括为三点:

  • 过渡期承诺:被弃用的 Scrapy 功能至少会被支持 1 年。举例:若某功能在 2020 年 6 月 15 日发布的版本中被弃用,则该功能在 2021 年 6 月 14 日及之前发布的版本中仍应可用。
  • 移除时机:一年后的任何新版本可以移除该弃用功能。
  • 移除必公示:每个版本中移除的所有弃用功能都会明确列在 发布说明 中。

docs/news.rst 中可以找到大量符合此策略的真实案例,例如:

  • CrawlerRunnerfrom_crawler() 之外的旧式 from_settings() 用法被弃用(Deprecations 小节);
  • 若干在 "deprecated in Scrapy 2.5.0 / 2.8.0 / 2.9.0 / 2.10.0 / 2.11.0" 中弃用的 API,在后续版本的 "Deprecation removals" 小节中被正式移除。

这些记录表明"弃用 → 至少一年支持 → 移除并在发布说明中注明"的流程在项目中是被严格执行的。

源码级实现:弃用机制是如何落地的

文档中"弃用功能"的承诺,在代码层面有一套完整的实现体系,主要由 scrapy/exceptions.pyscrapy/utils/deprecate.py 两个文件承载。

自定义警告类别:ScrapyDeprecationWarning

Python 内置的 DeprecationWarning 默认被解释器静默(只在 __main__ 中显示),这会导致弃用提示"用户根本看不到"。Scrapy 为此定义了专门的警告类别:

class ScrapyDeprecationWarning(Warning):
    """Warning category for deprecated features, since the default
    :exc:`DeprecationWarning` is silenced.
    """

所有弃用警告统一使用这个类别抛出,用户只需在 settings 或代码中对该类别做一次 warnings.filterwarnings 配置,即可精确控制弃用提示的显示策略,而不会被第三方库(如 Twisted)的弃用噪音干扰——事实上 scrapy/init.py 中就显式忽略了 Twisted 模块的 DeprecationWarning

弃用类工厂:create_deprecated_class

当 Scrapy 需要重命名一个基类时(比如把 ScrapyClientContextFactory 改名迁移),直接删除旧类会瞬间破坏所有继承它的用户代码。scrapy/utils/deprecate.py 中的 create_deprecated_class() 解决了这个问题,其行为是:

  • 旧类名被替换为一个"弃用壳"类,指向新的实现类;
  • 用户代码若继承旧类名,会在首次子类化时收到一次性警告("warning only on first subclass, there may be others");
  • 直接实例化旧类名时,每次都会收到警告;
  • 关键的是,它重写了 __subclasscheck__ / __instancecheck__,使得 isinstance(sub(), OldName) / issubclass(sub, OldName) 对新类的子类依然返回 True,保证基于类型判断的既有代码不会立即失效。

该机制在仓库中有真实使用实例,例如 scrapy/core/downloader/contextfactory.py 中的 ScrapyClientContextFactory = create_deprecated_class(...),以及 scrapy/core/downloader/tls.py 中的 ScrapyClientTLSOptions 弃用壳。

路径迁移规则:update_classpath 与 DEPRECATION_RULES

当类被整体移动到新模块时(如从 scrapy.core.downloader.contextfactory 迁往 scrapy.core.downloader.tls),设置项里配置的字符串路径需要被重定向。scrapy/utils/deprecate.py 提供了基于前缀替换的路径迁移表:

DEPRECATION_RULES: list[tuple[str, str]] = []

def update_classpath(path: Any) -> Any:
    """Update a deprecated path from an object with its new location"""
    for prefix, replacement in DEPRECATION_RULES:
        if isinstance(path, str) and path.startswith(prefix):
            new_path = path.replace(prefix, replacement, 1)
            warnings.warn(
                f"`{path}` class is deprecated, use `{new_path}` instead",
                ScrapyDeprecationWarning,
                stacklevel=2,
            )
            return new_path
    return path

DEPRECATION_RULES 是一个 (旧前缀, 新前缀) 二元组列表:加载组件时若配置路径命中旧前缀,就自动改写为新路径并抛出 ScrapyDeprecationWarning,明确告知用户应该更新哪个设置。这意味着即使你的 settings.py 中仍写着旧模块路径,Scrapy 也不会直接报 ImportError,而是先"带着警告继续工作"——这正是"弃用至少支持一年"策略在配置加载层的直接体现。

其他弃用辅助函数

scrapy/utils/deprecate.py 中还提供了:

  • attribute(obj, oldattr, newattr, version):当用户访问已改名的属性时抛出警告(见 L16-L23);
  • method_is_overridden(subclass, base_class, method_name):基于 __code__ 对象同一性判断某方法是否被子类重写,供"仅在你重写了该旧接口时才提示弃用"的场景使用;
  • argument_is_required(func, arg_name)(2.14 新增):判断函数参数是否必填,服务于签名演进时的兼容性检查;
  • warn_on_deprecated_spider_attribute(...)(见 L228-L235):针对 Spider 上被弃用的属性(建议改用 Spider.custom_settingsSpider.update_settings())的专项警告。

从源码结构看,这些工具函数共同构成了"弃用期"的完整基础设施:警告有独立类别、类有软迁移壳、路径有自动重定向、属性有访问陷阱——与文档承诺的"一年过渡期 + 发布说明公示"形成闭环。

实战建议:升级 Scrapy 前如何自查

基于文档承诺与仓库实现,升级前可执行如下检查:

  1. 确认当前与目标版本scrapy version(版本号来自 scrapy/VERSION)。
  2. 通读目标版本的发布说明:重点看 docs/news.rst 中对应版本的 Backward-incompatible changesDeprecation removals 小节。若你的代码使用了被移除项(例如旧的下划线接口、被删掉的命令行选项),必须先改造再升级。
  3. 打开弃用警告:在测试环境中将 ScrapyDeprecationWarning 设为可见(如 -W always::scrapy.exceptions.ScrapyDeprecationWarningwarnings.filterwarnings),运行完整爬取流程,收集所有命中项——这些就是你应当在新大版本之前完成的迁移清单。
  4. 不依赖下划线 API:审查项目代码与设置中是否引用了 _ 开头的函数/方法或内部模块(如 scrapy.utils.deprecate._clspath)。按 API 稳定性约定,这些随时可能变化。
  5. 区分生产版与开发版x.y 式发布可视为生产就绪;dev 后缀版本仅用于尝鲜和提前反馈问题,不应直接上生产。

小结

Scrapy 的版本管理由三条规则支撑:A.B.C 三段式中"主版本极少动、发布号可能含破坏性变更、修复号仅修 bug";1.0 之后所有版本生产就绪、单下划线 API 不承诺稳定;弃用功能至少支持一年且移除必在发布说明中公示。仓库源码(VERSION 文件ScrapyDeprecationWarningdeprecate 工具模块)证明这些承诺有具体的实现机制在背后保障。遵循"读发布说明、开弃用警告、不碰下划线 API"三步法,即可在 Scrapy 大版本升级时保持代码库的平稳迁移。

登录后查看全文
热门项目推荐
相关项目推荐