广告位 · 页头(后台-模块管理-新建模块后替换此区域)

如何在Linux服务器上设置一个自动抓取RSS的Cron Job?

RSS虽然不像过去那样被频繁提及,但仍是博客、新闻网站和技术社区提供内容订阅的重要方式。在Linux服务器上,我们经常需要自动聚合多个RSS源,把最新条目保存到本地数据库或文件,供后续展示、搜索或推送使用。cron是Linux系统内置的定时任务调度器,配合一个抓取脚本就能完成这个需求。本文将完整说明从编写脚本到配置cron任务、再到处理常见问题的全过程。

如何在Linux服务器上设置一个自动抓取RSS的Cron Job?

编写RSS抓取脚本

抓取RSS的基本思路是向目标URL发送HTTP请求,获取XML内容,然后解析出需要的字段。简单场景下可以使用curl下载原始XML文件,适合只做存档或后续统一处理的场景。如果需要把标题、链接、发布时间拆开存进数据库,用Python的feedparser库会更方便,它能够自动处理常见的RSS和Atom格式。

下面是一个基于Bash和curl的抓取脚本,它把RSS源保存为带时间戳的XML文件,并把结果写入日志。

#!/bin/bash
FEED_URL="https://ipipp.com/rss"
OUTPUT_DIR="/var/www/rss"
TIMESTAMP=$(date +%Y%m%d%H%M%S)
curl -s --max-time 30 "$FEED_URL" -o "$OUTPUT_DIR/feed_$TIMESTAMP.xml"
if [ $? -eq 0 ]; then
  echo "下载成功: $TIMESTAMP" >> /var/log/rss_fetch.log
else
  echo "下载失败: $TIMESTAMP" >> /var/log/rss_fetch.log
fi

这个脚本使用curl的静默模式和30秒超时,避免网络卡住;用时间戳命名文件防止覆盖;根据退出码记录日志。实际使用时需要把OUTPUT_DIR改成你的目标目录,并确保该目录存在且可写。

如果需要结构化存储,可以使用Python配合feedparser和sqlite3。以下脚本会解析RSS并把新条目插入SQLite数据库,利用主键去重。

import feedparser
import sqlite3
import sys
FEED_URL = "https://ipipp.com/rss"
DB_PATH = "/var/lib/rss/feeds.db"
def fetch_feed():
    data = feedparser.parse(FEED_URL)
    if data.bozo:
        print("解析出错:", data.bozo_exception, file=sys.stderr)
        return
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("CREATE TABLE IF NOT EXISTS entries (id TEXT PRIMARY KEY, title TEXT, link TEXT, published TEXT)")
    for entry in data.entries:
        c.execute("INSERT OR IGNORE INTO entries VALUES (?,?,?,?)", (entry.id, entry.title, entry.link, entry.get("published","")))
    conn.commit()
    conn.close()
    print(f"抓取完成,共 {len(data.entries)} 条")

feedparser在解析失败时会把bozo属性设为True,可以据此记录错误。SQLite表使用id字段作为主键,配合INSERT OR IGNORE可以避免相同条目被重复插入。数据库路径和目录需要提前创建并赋予正确的写入权限。

配置Cron定时任务

cron通过crontab文件管理定时任务,每行一个任务,格式为分、时、日、月、周加命令。五个时间字段支持星号表示任意值,也支持*/n表示每隔n个单位。例如*/30 * * * *表示每30分钟执行一次。需要注意cron使用服务器本地时间,如果系统时区设置不对,任务的触发时间也会偏移。

要添加任务,首先运行crontab -e打开编辑器,然后加入类似下面的条目。

# 编辑当前用户crontab
crontab -e
# 每30分钟执行一次,输出追加到日志
*/30 * * * * /usr/local/bin/rss_fetch.sh >> /var/log/rss_fetch.log 2>&1

命令部分必须使用绝对路径,因为cron执行环境可能没有当前用户的PATH变量,导致找不到脚本或外部命令。输出重定向让所有标准输出和错误都写入同一个日志文件,方便后续排查。添加完成后可以用crontab -l查看当前任务列表。

如果希望以独立用户身份运行任务,可以在/etc/cron.d/目录下创建配置文件,并在行首指定用户名。同时需要给脚本设置可执行权限,例如chmod +x /usr/local/bin/rss_fetch.sh。另外cron不会读取shell配置文件,因此脚本内要自包含所需环境变量,或者显式source对应的配置文件。

防止任务重叠与增强稳定性

如果一次抓取耗时较长,上一个任务还没结束下一个又启动,可能导致重复写入或资源冲突。解决方法是使用文件锁。在脚本开始时检查锁文件是否存在,不存在则创建,脚本退出时删除。利用trap命令可以确保脚本无论正常退出还是被中断都清理锁文件。

下面是一个改造后的Bash脚本,先获取锁再执行实际抓取逻辑。

#!/bin/bash
LOCKFILE="/tmp/rss_fetch.lock"
if [ -e "$LOCKFILE" ]; then
  echo "已有任务在运行,退出" >> /var/log/rss_fetch.log
  exit 1
fi
touch "$LOCKFILE"
trap "rm -f $LOCKFILE" EXIT
# 调用实际抓取脚本
/usr/local/bin/rss_fetch.sh

锁文件放在/tmp目录下可能会被系统清理,生产环境建议放到/var/run或专用目录。trap绑定EXIT信号,在脚本退出时执行删除命令,避免死锁。如果脚本内部还有网络请求,最好再设置一个整体超时,避免单次任务长时间不结束。

除了锁文件,还应该为curl或Python请求设置超时。curl使用--max-time参数,Python的requests库可以设置timeout参数。如果RSS源偶尔不可用,可以在脚本内加入重试逻辑,例如失败后等待5秒再尝试一次。日志方面除了输出重定向,也可以使用logger命令把关键事件写入系统日志,这样可以通过journalctl或syslog集中查看。

测试与排错

配置完成后不要直接等待cron触发,先手动执行脚本确认功能正常。如果手动可以运行而cron不执行,通常有以下原因:脚本没有执行权限;crontab条目格式错误;命令路径不正确;环境变量缺失导致命令找不到。可以使用绝对路径调用所有外部程序,或者在脚本开头主动export必要的PATH。

通过日志查看运行情况是最直接的排错方式。如果日志为空,需要检查cron服务是否启动。在Debian/Ubuntu上可以用systemctl status cron,在CentOS/RHEL上使用systemctl status crond。同时查看系统日志/var/log/syslog或/var/log/cron,里面可能会记录cron执行失败的具体原因。有的最小化发行版没有预装cron,需要先安装cronie或vixie-cron等软件包。

另一个常见问题是重复抓取或时间不准。确认cron的时区设置是否正确,以及服务器是否使用NTP同步时间。RSS源本身可能包含无效的发布日期字段,解析时需要做兼容处理。测试时可以用一个本地测试RSS文件,通过file协议或本地HTTP服务来验证脚本逻辑,等脚本稳定后再指向真实源。

通过以上步骤,一个稳定的RSS自动抓取任务就建立起来了。根据实际需求,还可以把抓取结果接入消息队列、邮件通知或前端展示。cron的灵活性在于可以组合多个脚本形成完整的数据流水线,把分散的RSS源集中管理起来。

广告位 · 文章底部(后台-模块管理替换此区域)