后端技术选型那些年,我在爬虫与稳定之间反复横跳

一台会思考的电脑
2026-05-18 12:00
阅读 1171

凌晨三点,娃终于睡了。我轻手轻脚地合上绘本,打开终端——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),但生产爬虫?门都没有。


给同行的一点建议

如果你也在做类似的技术决策,不妨问问自己:

  1. 这个技术,半夜三点出问题,你能快速修好吗?
  2. 团队里除了你,还有人懂它吗?
  3. 它的失败成本,是你能承受的吗?

别被“技术先进”迷惑。稳定、可维护、可监控,才是后端系统的生命线。

至于爬虫——它从来不是银弹,而是一把双刃剑。用得好,是情报利器;用不好,轻则 IP 被封,重则法律风险。务必做好:

  • 遵守 robots.txt
  • 控制请求频率
  • 不抓敏感数据
  • 加上明确的 User-Agent 标识

尾声:在尿布和代码之间找到平衡

写完这篇稿子,天快亮了。厨房飘来奶香味——老公在热奶瓶。我保存文件,git push origin main,顺手把爬虫的监控面板截图发到工作群:“今日抓取成功率 99.7%,异常商品已入 DLQ,待处理。”

然后关掉终端,轻手轻脚走进卧室。女儿翻了个身,小手攥着她最爱的编程兔玩偶(没错,我给她买的)。

有时候觉得,做妈妈和做程序员,其实很像:
都要耐心调试(不管是 Bug 还是情绪),
都要预判风险(空指针 or 发烧),
都要在混乱中建立秩序。

技术会过时,框架会更迭,但解决问题的能力,永远不过时

——一个在成都带娃写代码的 Vim 党,于晨光微熹时

评论 0

最热最新
暂无评论
一台会思考的电脑Lv.1
0
影响力
0
文章
0
粉丝