广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

实测 | 心理健康:副业开发

我用五个月时间在副业开发里摸爬滚打,最核心的收获是:副业开发不是搞点小工具就能糊弄过去的,必须把主副业的资源边界切割清楚。我用一个Node.js的爬虫项目作为例子,从零到收钱的过程里,踩了三次大坑,其中一次差点导致IP被封。技术细节上,我用的是Puppeteer配合MongoDB,用Kubernetes做容器编排,用AWS Lambda作

实测 | 心理健康:副业开发
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
我用五个月时间在副业开发里摸爬滚打,最核心的收获是:副业开发不是搞点小工具就能糊弄过去的,必须把主副业的资源边界切割清楚。我用一个Node.js的爬虫项目作为例子,从零到收钱的过程里,踩了三次大坑,其中一次差点导致IP被封。技术细节上,我用的是Puppeteer配合MongoDB,用Kubernetes做容器编排,用AWS Lambda作为触发点,让整个流程自动化。关键在于突发流量处理、异常重试和数据清洗策略,这些必须提前写进脚本。我的配置文件里设定了--concurrency=5,--timeout=30000,用的是axios拦截器,而不是fetch,因为它支持重试和超时。如果你也想做副业开发,记住:不能把主业务的代码库当副业的模板,要独立部署,独立维护,独立监控。

▌ 技术参考


副业开发本质上是资源有限下的快速试错,但你必须得知道哪些技术能用,哪些不能用。我第一项副业是用Puppeteer做亚马逊商品价格监控,结果发现它不能直接跑到服务器上,必须用无头模式运行,否则会被反爬。后来用Crawlee框架替代,因为它自带会话管理,还支持分布式爬虫,但也有局限,比如不能直接操作DOM。配置里用了--max-retries=3,--headers={"User-Agent": "Mozilla/5.0"},还设置了--proxy-url=https://proxy.example.com,避免被封IP。关键是得把爬虫脚本和主业务代码隔离,否则一旦出问题,主业务也会受影响。


我用MongoDB存数据,用的是GridFS,因为图片太大,不能直接存到集合里。写入时用了insertOne,但后来发现批处理更快,改成了bulkWrite,每次插入100条,效率提升50%以上。索引要建在商品ID和价格字段,不然查询会慢。用的是Capped Collection,这样数据不会无限增长,还能自动清理旧记录。监控方面用Prometheus+Grafana,但后来发现日志更关键,所以用Filebeat把日志打到ELK,实时看爬虫出错的地方。数据清洗要用正则表达式,比如用/\D+/g替换掉价格里的非数字,再用parseFloat处理。


爬虫脚本不能用fetch,必须用Puppeteer或者Crawlee,因为它们支持动态渲染。我之前用的是requests+BeautifulSoup,结果爬到一半网页加载失败,数据乱了。改用Crawlee后,脚本里加了waitUntil="networkidle2",这样等页面稳定再提取数据。但Crawlee的分布式部署需要自己搭Kubernetes,否则会死在本地。我用了minikube+Docker,配置了Deployment和Service,还加了Horizontal Pod Autoscaler,根据请求量自动扩容。部署时需要记得加--namespace=production,避免和测试环境搞混。如果不想折腾K8s,用Docker Compose也行,但性能不如K8s。


副业开发的代码必须独立,不能和主业务耦合。我之前把副业的API直接挂在主业务的服务器上,结果主业务服务器CPU爆掉,副业也随之崩溃。后来用Vercel单独部署,配置了子域名,还用了Environment Variables来区分环境。比如在.env文件里写了API_URL=https://api.example.com,这样便于切换。用的是Express框架,但后来发现Fastify更快,就改用Fastify+TypeScript,这样接口更稳定,类型检查也更严。部署命令是vercel deploy --name=price-tracker,每次更新会自动触发构建,还能做灰度发布。


数据源选择很重要,不能随便抓。我之前抓的是亚马逊的普通页面,结果被反爬,爬了三天没数据。后来改用Selenium配合Scrapy-Playwright,但发现Selenium太慢,就换成了Playwright。Playwright支持多浏览器,但配置时要指定--browser=chromium,否则会用firefox,识别不出亚马逊的验证码。另外,数据清洗不能只用正则,得加人工检查,比如用fs.readFileSync读取raw数据,再用JSON.parse处理。如果数据量大,用streaming的方式读取更高效,避免内存溢出。


监控系统必须实时,不能等出问题再查。我之前用的是日志文件,结果一个问题出现后,两天才发现。现在用的是Kubernetes的Prometheus Exporter,设置了CPU和内存的阈值,超过80%就自动扩容。还用Opentelemetry收集日志和追踪,把日志存到Loki,用Grafana展示。配置的时候用了--set=metrics.enabled=true,--set=logging.level=info。但发现Loki的查询效率不够,就改用Elasticsearch,但需要额外的配置,比如调整分片数和副本数,不然查询会变慢。


异常处理必须全面,不能只抓大问题。我之前用try/catch,但发现有些错误漏了,比如页面加载超时或者DOM元素不存在。后来用了Puppeteer的on('error')和on('pageerror'),还能设置--timeout=30000,避免卡死。但后来发现异常太多反而影响性能,就加了日志记录,用levels来区分严重程度。比如error级别的才记录,warning级别的只在本地打印。调试时用--debug=true,但生产环境必须关掉,用--log-level=error。另外,爬虫不能一直跑,得加定时任务,比如用node-schedule设置每小时跑一次。


副业开发不能依赖主业务的数据库,必须自己建。我之前用的是主业务的MySQL,结果数据混乱。后来单独建了MongoDB实例,用的是Atlas,付费版能自动备份。但在配置连接时,用了--username=admin和--password=secret,导致权限问题。后来改用IAM角色,通过AWS Secrets Manager获取凭证。连接字符串是mongodb+srv://cluster0.abcde.mongodb.net/mydb?authMechanism=SCRAM-SHA-256。数据同步用的是Change Streams,这样能实时监听数据库变化。但发现Change Streams在Atlas上支持不好,就改成每天定时备份到S3。


部署成本不能太高,否则副业就亏了。我之前用的是AWS EC2,但发现费用贵,CPU利用率低。后来改用Lambda,用的是Python 3.10运行时,配置了ConcurrentExecutions=5,这样并发更高。但Lambda的冷启动时间太长,用Requests请求会超时。后来用了AWS API Gateway做代理,配置了X-Amzn-Trace-Id和X-Forwarded-For,这样能更准确地追踪请求。成本方面,用了CloudWatch Logs+X-Ray,发现Lambda的执行时间太长,就加了--timeout=3000,但实际是10秒,得改优化代码。


爬虫必须支持重试,不能一次失败就放弃。我之前用的是简单的setTimeout,但发现有时候是网络波动,有时候是反爬策略。后来用的是axios的retry功能,配置了--max-retries=3,--retry-axios=true。还加了重试间隔,用--retry-axios-interval=1000。但后来发现每次重试都会增加IP的请求频率,容易被封。于是改用Crawlee的retry功能,支持自定义重试策略,比如在headers里加随机User-Agent,用--headers={"User-Agent": "Mozilla/5.0 (random)"}, 但实际是用了一个数组,每次随机选一个。这样既能重试,又能避免IP被封。

十一
数据存储不能只看容量,也要看性能。我之前用的是MongoDB的默认配置,结果取数据时经常卡。后来加了分片,用的是sharding,配置了--shards=2,--replicaSet=rs0。还用了索引,比如在price字段加了单字段索引,这样查询更快。但发现分片需要额外的配置,比如mongos和config servers,部署起来麻烦。后来改用RocksDB,用的是--engine=rocksdb,这样对磁盘IO更友好。不过RocksDB的查询性能不如MongoDB,只能作为缓存用。

十二
副业开发不是创业,不能追求完美。我一开始想做一个完整的系统,结果项目卡在原型阶段,没人用。后来改用最小可行产品,只做抓取和展示,用的是React+TypeScript,还用了TailwindCSS做UI。部署时用的是Vercel,配置了环境变量,比如API_KEY=xxx,这样在生产环境不用暴露敏感信息。但UI太丑,就加了Tailwind的dark mode,用--dark-mode=class,这样适配移动端更好。数据用的是SQLite,但发现并发问题,就改用PostgreSQL+pg-promise,配置了--max-connections=10,避免连接数爆掉。

十三
代码结构必须清晰,不能混乱。我之前把所有逻辑混在一起,结果改功能时总是出错。后来用了MVC模式,把控制器、服务、模型分开。用的是Express+Typescript,配置了--tsconfig,这样编译更稳定。还加了TypeORM,用的是--enable-migrations,自动管理数据库结构。但发现TypeORM在Lambda上效率低,就改成Prisma,用的是--prisma=prisma.schema,这样更轻量。代码里用了async/await,而不是Promise链,这样更易读。

十四
性能优化不能只看代码,还要看基础设施。我之前用的是单个Lambda函数处理所有任务,结果执行时间超时。后来改用多个Lambda函数,用的是--parallel=true,这样并发更高。还用了Redis缓存,用的是--redis-url=redis://localhost:6379,这样减少数据库压力。但发现Redis的写入速度不够,就加了本地缓存,用的是--cache=memory,这样减少网络延迟。监控系统用的是Datadog,配置了--apikey=xxx,这样能实时看执行时间和错误率。

十五
副业开发必须有应急方案,不能死等。我之前遇到服务器宕机,数据全丢了。后来加了备份机制,用的是AWS Backup,配置了--backup-frequency=hourly,--retention=7。还加了手动恢复流程,用的是rsync+SSH,这样能快速恢复。但人手不够,就加了自动化脚本,用的是crontab,--schedule="0 ",每小时备份一次。错误处理也加了自动恢复,用的是retry.js,配置了--retry=3,--backoff=1000,这样失败三次就自动重启服务。这些配置不能写成注释,必须写进代码里,否则会忘。