后端技术选型那些年,我在爬虫与稳定之间反复横跳
凌晨三点,娃终于睡了。我轻手轻脚地合上绘本,打开终端——vim ~/.zshrc,把刚才想到的几个参数调优记下来。成都的夜很静,键盘敲得噼里啪啦,像极了去年双11大促前那晚,我一边哄睡发烧的女儿,一边远程排查线上爬虫任务崩掉的事故。
说起来,我算个“非典型”后端工程师:白天是全职妈妈,晚上才是代码世界的战士。坐标成都,生活节奏慢,但技术迭代可一点不含糊。Vim 是我的主战场(IDE?那是什么?能热更新吗?),虽然偶尔也会被同事吐槽:“你这 .vimrc 比我们微服务还复杂”。
今天想聊聊我对技术探索与实践的看法,尤其是当后端开发遇上爬虫这种“灰色地带”的活儿时,如何在“玩新东西”和“别搞挂生产”之间走钢丝。
事情是从一个临时需求开始的
上个月,产品老大突然甩过来一个需求:我们要监控竞品的价格变动,每天抓取5000+商品页,实时告警异常波动。乍一听挺简单——不就是写个爬虫嘛?
但细想就冒冷汗:
- 这不是一次性脚本,是要长期跑在生产环境的
- 竞品有反爬机制,IP、User-Agent、请求频率都得动态调度
- 数据要进数仓,还得对接风控系统做异常检测
- 最关键的是:不能影响主业务链路
我第一反应是:“让数据团队干啊!”结果对方回我一句:“他们只管ETL,源头采集归你们后端。”
行吧,又是“谁靠近问题谁解决”的经典剧本。
技术选型:新潮 vs 老实
面对这种需求,我脑子里立刻蹦出两套方案:
方案A:用最新最酷的框架(比如 Scrapy + Redis + Playwright)
优点很明显:
- Scrapy 天生为爬虫设计,中间件、Pipeline、自动重试一应俱全
- Playwright 能搞定 JS 渲染页面,对付 SPA 不怕
- 分布式调度靠 Redis 队列,扩展性看起来不错
但现实很骨感:
- 团队没人熟悉 Playwright,上线后谁维护?
- Playwright 吃内存,一台机器跑不了几个实例
- 我们生产环境容器资源卡得死,运维一听“Headless Browser”就摇头
更惨的是,上周五我试着跑了个 demo,结果本地测试完一提交,CI 直接报错:
Error: Failed to launch browser! spawn /app/node_modules/playwright-core/.local-chromium/linux-XXXX/chrome ENOENT
查了半小时才发现 Docker 镜像没装 Chromium 依赖。那一刻我真的想砸电脑——娃还在隔壁哭,这边浏览器还起不来。
方案B:老老实实用 Go + Colly + 自建调度
Go 我熟,团队也有人会;Colly 轻量、高效、无头浏览器依赖;调度逻辑自己写,虽然糙点,但可控。
最关键的是:稳定。
我们后端主栈就是 Go,日志、监控、告警体系全打通。爬虫模块可以复用现有基础设施,不用额外申请资源、不用教运维新东西、不用写一堆文档解释“为什么我们要跑浏览器”。
权衡之后,我咬牙选了 B。
朋友问我:“你不是喜欢折腾新技术吗?”
我苦笑:“喜欢归喜欢,但半夜三点被 PagerDuty 叫醒的不是你。”
实战:在带娃间隙搭起一套抗压爬虫
架构其实很简单:
[Scheduler] → [Redis Queue] → [Worker Pool (Go + Colly)] → [Kafka] → [Data Warehouse]
但细节全是坑。
坑1:IP 封锁比想象中快
一开始我用单一出口 IP,跑了不到200个请求就被 403 了。后来接入代理池,但免费代理质量堪忧——延迟高、存活时间短、有些直接返回 502。
最后妥协方案:
- 优先用公司白名单 IP(申请了3个)
- 白名单打满后,切到付费静态住宅代理(贵,但稳)
- 每个 Worker 动态轮换 User-Agent 和请求间隔(随机 sleep 1~3s)
代码片段(简化版):
func fetchWithProxy(url string, proxyList []string) ([]byte, error) {
c := colly.NewCollector()
// 随机选代理
proxy := proxyList[rand.Intn(len(proxyList))]
c.SetProxy(proxy)
// 随机 UA
c.UserAgent = randomUA()
var body []byte
c.OnResponse(func(r *colly.Response) {
body = r.Body
})
// 随机延迟,避免规律性
time.Sleep(time.Duration(rand.Intn(2000)+1000) * time.Millisecond)
err := c.Visit(url)
return body, err
}
坑2:重试机制差点拖垮下游
最初没设重试上限,某个商品页 5xx 错误,Worker 无限重试,结果 Kafka 积压了几百万条重复消息。数仓同学直接在群里@我:“你这爬虫是在 DDOS 我们吗?”
赶紧加上:
- 单 URL 最多重试 3 次
- 失败任务进入 dead-letter queue,人工审核
- 成功抓取后加缓存,24小时内不重复抓
坑3:日志太多,磁盘爆了
Colly 默认打印大量 debug 日志。某天运维找上门:“你那个 pod 日均写 50G 日志,再这样要收钱了!”
关掉 debug,只记录 ERROR 和关键 METRIC:
c.SetLogger(&colly.StdLogger{Level: colly.LogError})
性能对比:新 vs 旧,真香还是翻车?
为了说服自己没选错,我做了个小对比(单机 4C8G):
| 指标 | Scrapy + Playwright | Go + Colly (无头) |
|---|---|---|
| 内存占用 | ~1.2 GB / 实例 | ~80 MB / 实例 |
| 并发能力 | 10-15 页面/秒 | 60-80 页面/秒 |
| JS 渲染支持 | ✅ 完美 | ❌ 仅静态 HTML |
| 反爬绕过难度 | 中(需额外处理) | 高(需手动模拟) |
| 团队维护成本 | 高(需学新生态) | 低(复用现有技能) |
| 上线部署复杂度 | 高(依赖 Chromium) | 低(纯二进制) |
结论很现实:如果目标页面是纯静态或简单 AJAX,Colly 完胜;如果是重度 SPA(比如 React/Vue 渲染的),才考虑 Playwright。
而我们抓的竞品,90% 是服务端渲染(SSR)或静态页——根本不需要浏览器!
带娃程序员的“技术哲学”
说实话,我现在对“新技术”越来越谨慎了。
不是不想玩,而是责任变了。以前单身时,我可以通宵调试 Rust 异步运行时,第二天顶着黑眼圈跟同事吹牛;现在?娃一咳嗽我就心慌,代码必须一次过,线上不能出事。
所以我现在的原则是:
探索归探索,生产归生产。
- 学习可以用 Python、Rust、Zig 玩爬虫玩具项目
- 但生产环境?Go、Java、Python(成熟库)优先
- 能不用浏览器就不用浏览器
- 能复用现有监控就绝不新建一套
上周我还用 Rust 写了个本地图片 EXIF 清理工具(顺便练 async),但生产爬虫?门都没有。
给同行的一点建议
如果你也在做类似的技术决策,不妨问问自己:
- 这个技术,半夜三点出问题,你能快速修好吗?
- 团队里除了你,还有人懂它吗?
- 它的失败成本,是你能承受的吗?
别被“技术先进”迷惑。稳定、可维护、可监控,才是后端系统的生命线。
至于爬虫——它从来不是银弹,而是一把双刃剑。用得好,是情报利器;用不好,轻则 IP 被封,重则法律风险。务必做好:
- 遵守
robots.txt - 控制请求频率
- 不抓敏感数据
- 加上明确的 User-Agent 标识
尾声:在尿布和代码之间找到平衡
写完这篇稿子,天快亮了。厨房飘来奶香味——老公在热奶瓶。我保存文件,git push origin main,顺手把爬虫的监控面板截图发到工作群:“今日抓取成功率 99.7%,异常商品已入 DLQ,待处理。”
然后关掉终端,轻手轻脚走进卧室。女儿翻了个身,小手攥着她最爱的编程兔玩偶(没错,我给她买的)。
有时候觉得,做妈妈和做程序员,其实很像:
都要耐心调试(不管是 Bug 还是情绪),
都要预判风险(空指针 or 发烧),
都要在混乱中建立秩序。
技术会过时,框架会更迭,但解决问题的能力,永远不过时。
——一个在成都带娃写代码的 Vim 党,于晨光微熹时

评论 0