個人でいくつかの情報サイトを自動運用しています。フレームワークを入れると更新のたびに依存やビルドが重くなるので、外部依存ゼロ(Nodeの標準モジュールのみ)の build.js 一枚で、データJSONから静的サイトを生成する構成にしました。加えて、自動生成にありがちな「事実の捏造」を防ぐための実在検証ガードを入れています。この記事はその設計の要点です。
データ駆動:JSON → build.js → public/
サイトの実体は2つのJSONだけです。
data/site.json … サイト設定(名前・カテゴリ・canonical 等)
data/articles.json … 記事の配列(slug/title/本文/出典/確認日 …)
│
node build.js … 依存ゼロ。記事HTML・一覧・タグ・sitemap・RSS・JSON-LD を出力
▼
public/ … Firebase Hosting へそのままデプロイ
build.js は fs と文字列テンプレートだけで完結します。テンプレエンジンも使いません。
const fs = require("fs");
const site = JSON.parse(fs.readFileSync("data/site.json", "utf8"));
const articles = JSON.parse(fs.readFileSync("data/articles.json", "utf8"));
validateArticles(articles); // ← 後述:実在検証ガード
for (const a of articles) {
const html = renderArticle(site, a); // 文字列テンプレでHTML生成
fs.mkdirSync(`public/articles/${a.slug}`, { recursive: true });
fs.writeFileSync(`public/articles/${a.slug}/index.html`, html);
}
writeSitemap(site, articles);
writeFeed(site, articles);
実在検証ガード:捏造をビルドで止める
自動生成は放っておくと「それっぽいが存在しない情報」を書きがちです。そこで、断定的な数値(割引率・価格・期限)を含む記事は、確認日(confirmed)と出典URL(source)を必須にし、無ければビルドを落とします。
function validateArticles(articles) {
for (const a of articles) {
const hasHardClaim = /\d+%|\d+円|まで|無料/.test(a.title + JSON.stringify(a.deal || ""));
if (hasHardClaim) {
if (!a.confirmed || !(a.source && a.source.length)) {
throw new Error(`実在検証NG: ${a.slug} は confirmed と source が必須です`);
}
}
}
}
「裏の取れていない断定はそもそも公開できない」状態をビルドで担保できるので、自動運用でも安心感があります。
SEO・アフィリエイトの扱い
- 記事HTMLには JSON-LD(BlogPosting/FAQPage/BreadcrumbList)・
canonical・sitemap.xml・RSS を自動出力。 - アフィリンクを使う場合は
rel="sponsored nofollow noopener"と開示文をbuild.jsが自動付与(規約対応をコード側で強制)。
※本記事はあくまで「生成の仕組み」の紹介で、記事内にアフィリンクは貼っていません。
デプロイ:独自サブドメインを量産
public/ を Firebase Hosting にデプロイし、Cloud DNS の CNAME で独自サブドメインを割り当てています。1サイト=1プロジェクトにすると分離しやすく、GA4はサービスアカウント経由でまとめて作成しています。
まとめ
「データJSON+依存ゼロのbuild.js+検証ガード」という素朴な構成でも、SEO要件を満たす静的サイトを安定して自動生成できました。実際に運用しているサイトの例として、お茶のセール情報を自動生成しているサイトを載せておきます: https://ocha.autoarticles.net (個人開発の自動運用サイトの一つです)。同じように軽量な自動生成を検討している方の参考になれば。
本記事はAI補助で執筆した、個人開発の紹介記事です。