写好一个Node.js爬虫脚本之后,很多人会发现一个问题:脚本在自己电脑上跑得好好的,一旦合上笔记本、关掉终端,爬虫就停了。想让爬虫每天定时抓取数据并持续写入数据库,必须把它部署到一台常年开机的服务器上,再配合定时任务让它自动执行。这篇文章就来完整讲讲从零开始把一个Node.js爬虫搬到Linux服务器并配置定时任务的全过程。

一、服务器环境准备与项目上传
部署的第一步是在服务器上装好Node.js运行环境。推荐使用nvm来管理Node版本,这样以后切换版本也很方便。先通过SSH登录服务器,执行以下命令:
# 安装 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc # 安装长期支持版 Node.js nvm install --lts node -v npm -v
确认版本号正常输出后,环境就算装好了。接下来把本地项目上传到服务器,常用的方式有两种:一种是用scp直接压缩上传,适合一次性部署;另一种是用git拉取代码,适合需要频繁更新的项目。如果你的爬虫脚本依赖puppeteer这类带浏览器内核的库,还需要在服务器上安装对应的系统依赖,否则运行时会报找不到共享库的错误。
# 方式一:本地打包上传(在本地执行) tar -czf spider.tar.gz spider/ scp spider.tar.gz root@192.168.0.1:/opt/ # 服务器上解压 cd /opt && tar -xzf spider.tar.gz # 方式二:git 拉取(在服务器执行) cd /opt git clone https://your-repo-address/spider.git
代码到位后进入项目目录执行npm install。这里有个细节要注意:如果项目里有puppeteer或canvas这类需要编译原生模块的依赖,建议加上--unsafe-perm参数,并且确保服务器已安装gcc、make和python3,否则安装过程会直接失败。
二、用pm2守护爬虫进程
爬虫部署好之后,直接用node index.js运行是有隐患的:SSH连接一断,进程就跟着没了。业界通用做法是用pm2来管理Node进程,它会守护进程、自动重启、记录日志,功能远超裸跑。
# 全局安装 pm2 npm install -g pm2 # 启动爬虫并命名 pm2 start index.js --name spider # 常用管理命令 pm2 list # 查看进程列表 pm2 logs spider # 查看实时日志 pm2 restart spider # 重启 pm2 stop spider # 停止 pm2 startup # 设置开机自启 pm2 save # 保存当前进程列表
需要理解的一点是,pm2适合守护"常驻型"爬虫,也就是脚本一直运行、内部自己控制抓取节奏的场景。但如果你的爬虫是"跑一次就退出"的批处理脚本,用pm2常驻反而是浪费资源,这种情况更适合下面讲的定时任务方案。另外,执行pm2 startup后终端会打印一条需要手动复制的命令,照着执行一次,pm2托管的服务才能在服务器重启后自动恢复,这一步很多人会漏掉。
三、两种定时任务方案:crontab与node-cron
方案一:系统级crontab
crontab是Linux自带的定时调度器,稳定可靠,最适合"定时执行一次性脚本"的场景。执行crontab -e编辑定时任务,写入如下内容:
# 每天早上 8 点执行一次爬虫,日志写入指定文件 0 8 * * * cd /opt/spider && /root/.nvm/versions/node/v20.11.0/bin/node index.js >> /opt/spider/spider.log 2>&1
这里有两个极易踩坑的点。第一,crontab的环境变量和你的SSH终端完全不同,PATH里往往没有node的路径,所以最好写node的绝对路径,可以用which node查出来。第二,一定要记得cd到项目目录再执行,否则脚本里如果用了相对路径读取配置文件或写入数据,会找不到文件。末尾的>> xxx.log 2>&1表示把标准输出和错误输出都追加到日志文件,排查问题时全靠它。
方案二:应用内node-cron
如果希望定时逻辑写在代码里,便于随项目一起维护,可以用node-cron这个库。先安装依赖npm install node-cron,然后在入口文件中编写调度逻辑:
const cron = require('node-cron');
const { runSpider } = require('./spider');
// 每天 8 点执行,五字段表达式和 crontab 语法一致
cron.schedule('0 8 * * *', async () => {
console.log('定时任务触发:', new Date().toLocaleString());
try {
await runSpider();
console.log('本轮抓取完成');
} catch (err) {
console.error('抓取失败:', err.message);
}
});
console.log('爬虫调度器已启动,等待下一次触发...');node-cron的优势是调度逻辑和业务代码耦合在一起,跨平台,本地开发时也能直接测试;缺点是必须依赖一个常驻进程,一旦进程挂了定时任务也就失效了,所以它通常要搭配pm2一起使用。两种方案的选择标准很简单:脚本跑完就退出选crontab,脚本内部有复杂调度逻辑选node-cron加pm2。
四、日志排查与常见问题处理
部署完成后不代表万事大吉,线上环境的问题往往在运行几天后才暴露。最常见的一类问题是爬虫在本地正常、上服务器就失败,原因多半出在IP差异上:服务器IP多为机房IP,容易被目标网站的风控拦截,表现为请求返回403或验证码页面。解决办法是降低请求频率、完善请求头伪装,必要时接入代理IP池。
另一类高频问题是时区。很多服务器的系统时区默认是UTC,你配置的"每天8点"实际是北京时间16点。检查时区可以用date命令,如果显示的不是CST,可以用timedatectl set-timezone Asia/Shanghai修正,或者干脆在代码里统一用时间库处理时区,避免依赖系统设置。
最后建议建立一套简单的监控习惯:每天看一眼日志文件的大小和最后更新时间,日志异常增长说明可能陷入了死循环,日志长时间不更新说明定时任务可能没触发。也可以用grep ERROR /opt/spider/spider.log | tail -20快速抽取最近的错误信息。把这几个环节都跑顺之后,你的爬虫就能在服务器上无人值守地稳定工作了。