用 Claude Code 构建博客 CMS:Astro MDX、多语言 SEO、质检与变现流程
用 Claude Code 和 Astro MDX 搭建博客 CMS,把内容结构、多语言、SEO、质检和 CTA 串成流程。
博客 CMS 不是写作工具,而是内容变现系统
很多技术博客的问题不是写得太少,而是发布流程太松。文章没有固定结构,翻译不同步,description 太长,代码没有验证,RSS 和 sitemap 没有更新,最后也没有把读者引导到产品、培训或咨询。
这里的 CMS 指 Content Management System,也就是内容管理系统:把文章按照固定格式保存、编辑、检查、预览和发布。它不一定一开始就是复杂后台。对 Astro 站点来说,Content Collections 加 MDX 已经可以形成一个轻量但严格的文件型 CMS。Claude Code 的价值,是把写作、翻译、代码检查、SEO 审核和 CTA 设计放进同一个编辑流程。
可以先阅读相关主题:Claude Code 与 Contentful CMS、内容漏斗审计、RSS 实现和站点地图生成。官方资料建议直接看 Astro 的 Content Collections、MDX integration 与 Sitemap integration。
先把 CMS 职责拆开
不要只对 Claude Code 说“做一个博客 CMS”。这个提示太宽,容易生成混合了数据库、后台、API、预览和发布逻辑的大块代码。文件型 CMS 更适合拆成可检查的责任。
| 模块 | 作用 | 适合交给 Claude Code 的任务 |
|---|---|---|
| 内容 schema | 规定 frontmatter 字段和类型 | 编写 content.config.ts,修复类型错误 |
| MDX 文章 | 管理正文、表格、代码和 CTA | 改写草稿,补充示例 |
| 多语言 | 同一个 slug 覆盖所有语言 | 检查缺失语言,自然本地化 |
| 预览 | 发布前确认页面显示 | 启动 dev server,检查链接和排版 |
| SEO | 管理标题、description、OGP、sitemap | 审核元数据和过长描述 |
| QA gate | 在发布前挡住问题 | 写校验脚本,运行构建 |
| 变现 | 从文章导向产品、培训或咨询 | 检查 CTA 和转化路径 |
三个真实用例
第一个用例是个人开发者的模板销售。写 Claude Code 教程时,可以在 CMS 中加入 ctaLabel 和 ctaUrl,让文章自然连接到付费模板、提示词库或小型课程。
第二个用例是公司技术博客。企业最怕发布看似正确但实际过时的内容,例如 API 名称已变、示例代码无法运行、只有日文更新而英文旧版仍在线。Claude Code 应该负责发布前检查,而不只是生成文章。
第三个用例是多语言 SEO。10 种语言要共用同一个 slug,比如 claude-code-blog-cms.mdx。标题可以本地化,但核心观点、代码示例、CTA 意图要保持一致,否则搜索流量和读者体验都会断裂。
site/src/content/blog/claude-code-blog-cms.mdx
site/src/content/blog-en/claude-code-blog-cms.mdx
site/src/content/blog-zh/claude-code-blog-cms.mdx
site/src/content/blog-ko/claude-code-blog-cms.mdx
site/src/content/blog-es/claude-code-blog-cms.mdx
site/src/content/blog-fr/claude-code-blog-cms.mdx
site/src/content/blog-de/claude-code-blog-cms.mdx
site/src/content/blog-pt/claude-code-blog-cms.mdx
site/src/content/blog-hi/claude-code-blog-cms.mdx
site/src/content/blog-id/claude-code-blog-cms.mdx
可复制的 Astro 内容 schema
schema 是“内容字段必须长什么样”的约定。下面的代码可以作为 src/content.config.ts 的起点使用。
// src/content.config.ts
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";
const blogSchema = z.object({
title: z.string().min(20).max(80),
description: z.string().min(40).max(120),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date(),
category: z.enum(["getting-started", "tips-and-tricks", "use-cases", "comparison", "advanced"]),
tags: z.array(z.string()).min(2).max(8),
heroImage: z.string().startsWith("/images/"),
draft: z.boolean().default(false),
requireAllLocales: z.boolean().default(false),
lang: z.enum(["ja", "en", "zh", "ko", "es", "fr", "de", "pt", "hi", "id"]),
ctaLabel: z.string().max(40).optional(),
ctaUrl: z.string().url().optional(),
});
const makeBlogCollection = (base: string) =>
defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base }),
schema: blogSchema,
});
export const collections = {
blog: makeBlogCollection("./src/content/blog"),
"blog-en": makeBlogCollection("./src/content/blog-en"),
"blog-zh": makeBlogCollection("./src/content/blog-zh"),
"blog-ko": makeBlogCollection("./src/content/blog-ko"),
"blog-es": makeBlogCollection("./src/content/blog-es"),
"blog-fr": makeBlogCollection("./src/content/blog-fr"),
"blog-de": makeBlogCollection("./src/content/blog-de"),
"blog-pt": makeBlogCollection("./src/content/blog-pt"),
"blog-hi": makeBlogCollection("./src/content/blog-hi"),
"blog-id": makeBlogCollection("./src/content/blog-id"),
};
MDX frontmatter 示例
MDX 可以理解为“能使用组件的 Markdown”。frontmatter 是文件顶部的元数据区,负责告诉列表页、RSS、OGP 和 sitemap 如何展示这篇文章。
---
title: "用 Claude Code 构建博客 CMS:Astro MDX、多语言 SEO、质检与变现流程"
description: "用 Claude Code 和 Astro MDX 搭建博客 CMS,把内容结构、多语言、SEO、质检和 CTA 串成流程。"
pubDate: "2025-12-22"
updatedDate: "2026-06-02"
category: "use-cases"
tags: ["Claude Code", "CMS", "博客运营", "Astro", "MDX"]
heroImage: "/images/hero/hero-036.png"
lang: "zh"
ctaLabel: "预约 Claude Code 内容运营咨询"
ctaUrl: "https://example.com/consulting"
---
## 第一节
本文把写作、翻译、SEO、质检和变现 CTA 放进同一个发布流程。
Node 发布前校验脚本
保存为 scripts/validate-blog-cms.mjs,在站点根目录运行 node scripts/validate-blog-cms.mjs claude-code-blog-cms。
// scripts/validate-blog-cms.mjs
import fs from "node:fs";
import path from "node:path";
const slug = process.argv[2];
if (!slug) {
console.error("Usage: node scripts/validate-blog-cms.mjs <slug>");
process.exit(1);
}
const locales = [["blog", "ja"], ["blog-en", "en"], ["blog-zh", "zh"], ["blog-ko", "ko"], ["blog-es", "es"], ["blog-fr", "fr"], ["blog-de", "de"], ["blog-pt", "pt"], ["blog-hi", "hi"], ["blog-id", "id"]];
const root = path.join(process.cwd(), "src", "content");
const failures = [];
function readFrontmatter(source) {
const match = source.match(/^---\n([\s\S]*?)\n---/);
if (!match) return {};
return Object.fromEntries(match[1].split("\n").flatMap((line) => {
const index = line.indexOf(":");
if (index === -1) return [];
return [[line.slice(0, index).trim(), line.slice(index + 1).trim().replace(/^"|"$/g, "")]];
}));
}
for (const [dir, lang] of locales) {
const file = path.join(root, dir, `${slug}.mdx`);
if (!fs.existsSync(file)) failures.push(`${dir}: missing file`);
const source = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
const data = readFrontmatter(source);
if (data.lang !== lang) failures.push(`${dir}: wrong lang`);
if (!data.updatedDate) failures.push(`${dir}: missing updatedDate`);
if ((data.description || "").length > 120) failures.push(`${dir}: description too long`);
if (!/https:\/\/docs\.astro\.build/.test(source)) failures.push(`${dir}: missing official docs`);
if (!/\]\(\/blog\/claude-code-/.test(source)) failures.push(`${dir}: missing internal link`);
if (!/(CTA|咨询|consult|training)/i.test(source)) failures.push(`${dir}: missing CTA`);
if ((source.match(/`{3}/g) || []).length < 6) failures.push(`${dir}: fewer than three code blocks`);
}
if (failures.length) {
console.error(failures.map((item) => `- ${item}`).join("\n"));
process.exit(1);
}
console.log(`OK: ${slug} passed localized CMS checks.`);
给 Claude Code 的发布提示词
Rewrite the article for slug <slug> as a production-ready Astro MDX blog post.
- Edit only the localized files for this slug.
- Keep heroImage and category.
- Add updatedDate: "2026-06-02".
- Keep description within 120 characters.
- Include 3 real use cases, concrete pitfalls, runnable code, official Astro docs, internal links, and a monetization CTA.
- Report changed files and focused check results.
运营指标要提前写进 CMS
真正能变现的博客 CMS,不只保存标题和正文,还要让编辑在发布前看到“这篇文章为什么值得发”。建议在 frontmatter 或旁边的运营表里记录目标关键词、读者阶段、主要 CTA、关联商品、预计内部链接和复盘日期。这样 Claude Code 在改稿时不会只追求字数,而会围绕搜索意图和商业动作补内容。
例如一篇 Claude Code 入门文,读者可能还没有预算,只需要安全的第一步;这时 CTA 应该指向免费清单或训练营说明。另一篇 AWS Lambda 实战文,读者已经在排查部署问题,CTA 就可以指向咨询或模板包。CMS 把这些字段固定下来,团队就能比较不同文章的转化,而不是只看 PV。
发布后的复盘也要进入流程。每周检查 Search Console 的查询词、Cloudflare Web Analytics 的国家分布、训练页点击率和邮件咨询数。若文章有展示但点击率低,先改标题和 description;若点击进来马上离开,补导入文、图表和代码验证;若读完不咨询,说明 CTA 与读者阶段不匹配。Claude Code 可以负责生成修改建议,但最终判断要基于数据。
编辑审稿时要看的四个信号
第一,看文章是否有自己的经验。只是复述官方文档,很容易被判断为低价值内容。第二,看代码是否能在本地复制执行,至少要说明依赖、目录和运行命令。第三,看多语言版本是否本地化,而不是把日语例子直译到所有市场。第四,看内部链接是否真的帮助读者继续学习,而不是为了 SEO 生硬堆链接。
如果这些信号都通过,再让 Claude Code 做标题候选、FAQ、比较表和结构化摘要,效果会更稳定。反过来,如果原稿没有读者问题、没有实际验证、没有下一步行动,AI 再润色也只是把薄内容写得更流畅,不能真正提高转化。
常见失败模式
第一,只有主语言更新,其他语言停留在旧代码。第二,Claude Code 的写入范围太大,误改其他 slug。第三,代码块是伪代码,读者复制后无法运行。第四,CTA 太硬,只像广告,不像下一步帮助。第五,更新日期只是机械修改,没有重新确认官方文档和实际代码。
实际验证中,schema 可以挡住缺失日期,Node 脚本可以发现过长 description、缺少官方链接、缺少内部链接和 CTA。机器检查无法判断中文是否自然,也无法判断咨询导线是否有说服力,所以发布前仍需要人工读一遍。对于想把技术博客变成培训、模板销售或顾问业务入口的团队,先搭好这个 CMS 和 QA gate,比单纯增加文章数量更有效。
免费 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 与咨询路径都要可审查。