用 Claude Code 实现 RSS Feed:静态站点 RSS 2.0 与 Atom 实战
用 Claude Code 生成 RSS 2.0/Atom,处理 XML 转义、日期、绝对 URL、多语言、验证与缓存。
RSS 仍然是技术内容的稳定入口
发布文章不等于读者一定会看到。搜索、社交平台、邮件、Slack、RSS 阅读器都会影响传播。RSS feed 是一个机器可读的 XML 文件,用来列出文章标题、链接、摘要、发布时间和分类。它不像社交平台那样依赖算法排序,对开发者、研究员、编辑和内部知识库仍然很实用。
XML 的规则比普通 HTML 更严格。标题里一个没有转义的 & 就可能让整个 feed 失效。Atom 是另一种订阅格式,由 RFC 4287 定义。初学者可以先实现 RSS 2.0,再按需求补 Atom。
本文用 Claude Code 实现静态站点 feed,但不会只写“让 AI 生成 RSS”。我们会处理 RSS 2.0、Atom、XML 转义、日期格式、绝对 URL、多语言 feed、验证、缓存,以及给 Claude Code 的审查提示词。相关内容可继续阅读 Claude Code 博客 CMS、Claude Code SEO 优化 和 Claude Code sitemap 生成。
实现时以一次资料为准:RSS 2.0 看 RSS Advisory Board 规范,Atom 看 IETF RFC 4287,验证用 W3C Feed Validation Service,Astro 项目看 Astro RSS recipe。
三个真实使用场景
第一个场景是技术博客的复访。很多高级读者会把常读网站加入 Feedly、Inoreader 或自建阅读器。只要 feed 正常,新文章就会进入他们的阅读流程,而不是依赖某条社交动态是否被看到。
第二个场景是公司内部知识更新。发布说明、事故复盘、安全公告、架构决策记录都可以共用一个 feed。Slack bot、内部门户、状态页读取同一份 XML,作者只维护一份文章数据。
第三个场景是多语言 SEO。一个站点如果同时有中文、日文、英文、西班牙文等内容,就不应该只提供一个混合 feed。/zh/rss.xml 应只包含中文文章,/en/rss.xml 应只包含英文文章。collection、URL 前缀和 language 必须一致。
第四个场景是商业转化。ClaudeCodeLab 的文章会连接到模板、产品、培训和咨询。如果新文章没有进入 RSS,老读者就少一个触点。Claude Code 培训与咨询 这样的 CTA 应该作为自然下一步出现,而不是硬插广告。
先写清楚 feed 契约
在让 Claude Code 修改代码前,先决定规则。很多问题都来自需求太模糊:相对 URL、草稿泄露、XML 损坏、日期排序错误、不同语言混在一起。
| 项目 | 推荐做法 | 原因 |
|---|---|---|
| 格式 | 先 RSS 2.0,必要时加 Atom | 支持广,实现简单 |
| URL | 全部使用绝对 URL | 外部阅读器才能稳定打开 |
| 日期 | RSS 用 toUTCString(),Atom 用 toISOString() | 更接近常见规范格式 |
| 内容 | 先输出 description | 全文 HTML 需要 sanitize 和图片 URL 处理 |
| 数量 | 20 到 50 篇 | 避免 feed 过大 |
| 缓存 | 设置明确缓存策略 | 发布后不会长时间看不到更新 |
| 验证 | 本地脚本加 W3C Validator | 浏览器能打开不代表规范正确 |
RSS 2.0 的核心是 channel 和 item。channel 描述站点,item 描述每篇文章。guid 用来稳定识别文章,通常可以使用文章永久链接。
无依赖 RSS 生成脚本
下面的脚本保存为 scripts/generate-rss.mjs,执行 node scripts/generate-rss.mjs 后会生成 dist/rss.xml。真实项目中只需要把 posts 替换为 MDX 或 CMS 数据。
// scripts/generate-rss.mjs
import fs from "node:fs";
import path from "node:path";
const siteUrl = "https://example.com";
const outputPath = path.join(process.cwd(), "dist", "rss.xml");
const posts = [
{
title: "用 Claude Code 实现 RSS Feed",
description: "为静态站点生成并验证 RSS 2.0 feed。",
slug: "claude-code-rss-feed",
pubDate: "2026-06-02T09:00:00+09:00",
tags: ["Claude Code", "RSS"],
},
];
function escapeXml(value) {
return String(value ?? "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function toRssDate(value) {
const date = new Date(value);
if (Number.isNaN(date.getTime())) throw new Error(`Invalid date: ${value}`);
return date.toUTCString();
}
const items = posts.map((post) => {
const url = new URL(`/zh/blog/${post.slug}/`, siteUrl).toString();
return ` <item>
<title>${escapeXml(post.title)}</title>
<link>${url}</link>
<guid isPermaLink="true">${url}</guid>
<description>${escapeXml(post.description)}</description>
<pubDate>${toRssDate(post.pubDate)}</pubDate>
</item>`;
}).join("\n");
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>ClaudeCodeLab 中文</title>
<link>${siteUrl}/zh/</link>
<description>Claude Code 实战教程与工作流</description>
<language>zh</language>
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
<ttl>60</ttl>
${items}
</channel>
</rss>
`;
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, xml, "utf8");
console.log(`Generated ${outputPath}`);
这里最重要的是 escapeXml()、new URL() 和日期校验。Claude Code 重构时不能删掉这些保护。
Astro 项目的实现
Astro 项目可以使用官方的 @astrojs/rss。先确认 astro.config.mjs 中设置了 site。
npm install @astrojs/rss
// src/pages/zh/rss.xml.ts
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
export async function GET(context: { site: URL }) {
const posts = await getCollection("blog-zh", ({ data }) => !data.draft);
const items = posts
.sort((a, b) => {
const aDate = new Date(a.data.updatedDate ?? a.data.pubDate).getTime();
const bDate = new Date(b.data.updatedDate ?? b.data.pubDate).getTime();
return bDate - aDate;
})
.slice(0, 30)
.map((post) => ({
title: post.data.title,
description: post.data.description,
pubDate: post.data.updatedDate ?? post.data.pubDate,
link: `/zh/blog/${post.id}/`,
categories: post.data.tags,
}));
return rss({
title: "ClaudeCodeLab 中文",
description: "Claude Code 实战教程与工作流",
site: context.site,
items,
customData: "<language>zh</language><ttl>60</ttl>",
});
}
在页面布局的 head 中加入自动发现链接:
<link rel="alternate" type="application/rss+xml" title="ClaudeCodeLab RSS" href="/zh/rss.xml" />
<link rel="alternate" type="application/atom+xml" title="ClaudeCodeLab Atom" href="/zh/atom.xml" />
全文 RSS 可以做,但需要处理 HTML sanitize、相对图片、组件和广告脚本。初版建议只放 description,稳定后再扩展。
Atom、多语言与验证
Atom 的 id 和 updated 要稳定,日期用 ISO 字符串更简单。
function atomEntry(post, siteUrl) {
const url = new URL(`/zh/blog/${post.slug}/`, siteUrl).toString();
return ` <entry>
<title>${escapeXml(post.title)}</title>
<link href="${url}" />
<id>${url}</id>
<updated>${new Date(post.pubDate).toISOString()}</updated>
<summary>${escapeXml(post.description)}</summary>
</entry>`;
}
多语言 feed 要把 collection、prefix、language 绑定在一起:
const feeds = [
{ collection: "blog", prefix: "", language: "ja", title: "ClaudeCodeLab" },
{ collection: "blog-en", prefix: "/en", language: "en", title: "ClaudeCodeLab English" },
{ collection: "blog-zh", prefix: "/zh", language: "zh", title: "ClaudeCodeLab 中文" },
];
本地验证脚本可以先挡住明显错误:
// scripts/check-feed.mjs
const feedUrl = process.argv[2] ?? "http://localhost:4321/zh/rss.xml";
const response = await fetch(feedUrl);
const xml = await response.text();
const failures = [];
if (!response.ok) failures.push(`HTTP status is ${response.status}`);
if (!xml.includes("<rss")) failures.push("missing rss root");
if (!xml.includes("<channel>")) failures.push("missing channel");
if (!xml.includes("<item>")) failures.push("missing item");
if (/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[a-fA-F0-9]+;)/.test(xml)) failures.push("unescaped ampersand");
if (!/<guid[^>]*>https?:\/\//.test(xml)) failures.push("guid should be absolute");
if (failures.length) {
console.error(failures.map((failure) => `- ${failure}`).join("\n"));
process.exit(1);
}
console.log(`OK: ${feedUrl}`);
常见失败包括:R&D 没有转义导致 XML 失效;草稿文章进入 feed;相对 URL 在外部阅读器中打不开;日期格式模糊导致排序异常;中文 feed 链接到英文页面;CDN 缓存太久导致发布后看不到更新。这些都应该写进 Claude Code 的 review 条件。
Claude Code 提示词与实际验证
实现提示词可以这样写:
为 Astro 静态站点实现 RSS 2.0。
- 只编辑 src/pages/zh/rss.xml.ts。
- 使用 @astrojs/rss。
- 排除 draft。
- 按 updatedDate 或 pubDate 从新到旧排序。
- 限制 30 篇。
- 使用正确的 /zh/blog/ URL 前缀。
- 加入 language 和 ttl。
- 最后报告验证命令和结果。
审查提示词要更批判:
请批判性审查 RSS 实现。
优先检查 XML 转义、相对 URL、draft 泄露、日期格式、guid 稳定性、多语言 URL 前缀、缓存和 W3C 验证风险。
我在一个小型 Astro 数据集上试过这套流程。本地脚本能抓到没有转义的 & 和缺失的绝对 guid,W3C Feed Validator 能补充格式检查。人工仍然要看翻译是否自然、CTA 是否匹配读者意图,以及 feed 是否真的出现在阅读器中。团队如果要把 RSS、sitemap、文章 QA 和商业转化串起来,可以从 Claude Code 培训与咨询 开始。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。