Published on

从 Scrapy 到自研:单人长期项目中的爬虫工程判断

Reading time
5 分钟
Page view
-
作者

这篇文章并不是一次框架优劣的技术对比,也不是一份自研爬虫的实现说明。 它记录的是在一个单人、长期维护的项目中,我如何重新评估工程风险,并逐步意识到:相比功能完备,一个更可控的系统才是我真正需要的东西。 从 Scrapy 到自研,并不是技术升级,而是一次关于工程判断的转向。

为什么一开始选择 Scrapy

在立项之初进行技术选型时,我需要一个能够快速启动的爬虫框架进行 MVP 的验证。 考虑到项目定位是长期维护且无盈利的单人项目,我必须严格控制开发成本和时间投入。

Scrapy Cloud (Zyte) 非常慷慨地向“只要曾经是 GitHub Education 成员”的用户提供了一个永久免费的单元, 最终我将目光转向了 Scrapy,这直接满足我在成本和启动速度上的核心需求,让我能够在不增加额外成本的情况下,对 MVP 进行快速验证。

问题的出现方式:不是一次失败,而是持续摩擦

在 MVP 阶段,Scrapy 帮助我快速实现了预期功能。 但是随着项目规模的增长,Scrapy 的各个模块体积逐渐变大,一些小改动开始牵动多个模块,需要解决的外部依赖也越来越多。 在间隔长达数月后重新回到项目时,我需要在修改和新增功能时更频繁地重新审视历史实现。

真正让我警觉的,并不是某个具体的错误,而是我开始不再确信自己能够持续、可预期地控制这个系统。

三个我无法承担的工程风险

依赖不可控:单人项目最致命的风险

在 Scrapy 编译部署的过程中,需要引入非业务相关的第三方依赖,其中部分依赖之间会产生版本冲突,导致部署失败。 随着业务变化,这类冲突的频率也在增加,而问题的解决往往依赖外部生态的维护状态。

作为单人开发者,我无法预测也无法控制这些外部依赖的更新节奏,项目推进因此变得不稳定。 对于单人长期项目而言,需要“等待外部修复”的问题,是不可接受的系统性风险。

维护模型不匹配:单人项目分身乏术

在项目实际运行中,爬虫运行错误或数据异常、缺失的问题,通常需要我主动检查日志才能发现并修复。 在长期无人值守的状态下,项目并不能将问题主动暴露出来,而是隐含地假设存在一名长期高频关注它的维护者,这一假设在单人项目中并不成立。

实际上,我需要的是“无人值守也能暴露问题的系统”,而不是“功能齐全但需要我持续关注的系统”,人工监控依赖,是单人项目无法长期承担的维护模型。

结构逐渐僵硬:强约定开始侵蚀边界

Scrapy 中,数据在爬取完成后从 spider 流向 item pipeline 进行处理。 随着数据处理复杂度增加,pipelines 逐渐变成“万能处理区”,单一管道承担了过多职责。

同时,按序执行的模型使得修改路径不断拉长,长此以往,处理单元之间的边界开始变得模糊,从而引发更多的耦合和冲突。

当系统的主数据流被固定在一个强约定通道中时,结构便失去了演进空间,系统性风险也随之逐渐积累。

不追求更复杂,而是更可控

面对上述风险,我决定自研一个更可控的爬虫系统。 自研并不是为了追求更复杂的功能,分布式、高并发等特性听起来很吸引人,但对我当前的项目并不构成现实约束。

我真正追求的是一个依赖可控、维护模型契合实际需求、结构清晰的系统,这不是一次技术升级,而是在既定资源约束下,对工程风险做出的重新分配。

三条设计底线,决定了框架的形态

  • 功能可以少,但依赖不能失控

我可以接受高级又复杂的功能缺失,但是拒绝不可控的外部依赖导致的长期不确定性。

  • 早期多投入维护,换取后期低心智负担

我愿意将运行管理前置,在早期投入更多精力进行监控和告警设计,换取后期更低的心智负担和维护成本。 错误需要被系统化整理,而不是依赖人工发现。

  • 边界必须清晰,哪怕开发效率受限

我需要一个边界明确、单元职责单一的系统架构,拒绝向“万能处理区”临时塞逻辑,哪怕这意味着在短期内牺牲部分开发效率。

一个只服务于自己的爬虫框架

这个框架并不试图成为一个更通用的爬虫框架,它只是试图在一个单人长期项目中,减少我在长期维护过程中每天需要做出的工程决策数量


参考资料