セールやお得情報を扱うサイトで最悪なのは、実在しない割引を「半額」「50%OFF」と断定してしまうことだ。信頼を失ううえ、アフィリエイトの規約面でもリスクになる。人手のレビューだけでは、たまに裏取りの無い断定がすり抜ける。
自分が運用しているKindleマンガのセール速報サイト「マンガセール速報」(manga.autoarticles.net)では、この事故をビルド時の検証ゲートで機械的に防いでいる。方針はシンプルだ。
断定的な割引・価格表現を含む記事は、confirmed(確認日)と出典URL(source)の両方が揃っている場合だけ公開を許す。揃っていなければビルドを落とす。
どんな表現を「断定」とみなすか
「50%OFF」「半額」「◯◯円→◯◯円」「全巻99円」といった、特定商品の割引・価格を言い切る表現をまとめて正規表現で拾う。実際にサイトで使っている判定式がこれだ。
1 2 3
| const assertive = /(\d{1,3}\s*%\s*(OFF|オフ|還元(?!率))|半額|実質\s*無料|今だけ半額|→\s*\d+\s*円|\d+\s*円均一|全巻\s*\d+\s*円|各\s*\d+\s*円)/;
|
ポイントは 還元(?!率) の否定先読みだ。「還元」(=具体的なポイント還元の断定)は検知したいが、「還元率」という一般語の解説はゲート対象にしたくない。ここを区別しないと、仕組み解説の記事まで巻き込んで落ちてしまう。
検証ゲート本体
記事1件ずつをJSON文字列化して assertive にかけ、ヒットしたら confirmed と有効な出典URLの両方を要求する。どちらか欠ければ問題として積み、1件でもあればプロセスを異常終了させる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const fs = require("fs");
function validateArticles(articles) { const assertive = /(\d{1,3}\s*%\s*(OFF|オフ|還元(?!率))|半額|実質\s*無料|→\s*\d+\s*円|全巻\s*\d+\s*円|各\s*\d+\s*円)/; const isUrl = (s) => typeof s === "string" && /^https?:\/\//.test(s); const problems = [];
for (const a of articles) { const blob = JSON.stringify(a); const sources = [].concat(a.source || [], a.sourceUrl || []).filter(Boolean); const hasSource = sources.some(isUrl);
if (assertive.test(blob) && (!a.confirmed || !hasSource)) { problems.push(`[NG] ${a.slug}: 断定価格には confirmed と出典URL が必須`); } if (!a.confirmed && !a.date) { problems.push(`[NG] ${a.slug}: 確認日(confirmed/date)が無い`); } if (sources.length && !hasSource) { problems.push(`[NG] ${a.slug}: source が有効なURLでない`); } }
if (problems.length) { console.error("セール実在検証に失敗:\n" + problems.join("\n")); process.exit(1); } }
const articles = JSON.parse(fs.readFileSync("data/articles.json", "utf8")); validateArticles(articles); console.log("OK: 全記事が検証を通過");
|
手元で試す
適当なJSONを作って落ちることを確認する。
1 2 3 4 5 6 7 8 9 10 11
| mkdir -p data cat > data/articles.json <<'JSON' [ {"slug":"ng","title":"全巻50%OFF","source":[],"confirmed":null}, {"slug":"ok","title":"全巻50%OFF","confirmed":"2026-07-14", "source":["https://example.com/sale"]} ] JSON node validate.js
|
ng は出典も確認日も無いので落ち、ok は両方揃っているので通る。断定表現を消して「仕組みの一般解説」に書き換えても通る。つまりこのゲートは、言い切るなら裏を取れという編集方針をコードに落としたものだ。
効いているポイント
- レビュー漏れがあっても、裏付けの無い断定は本番に出る前に止まる。
build.js の一部として走らせれば、デプロイ手順に自然に組み込まれる(別途CIを足す必要すらない)。
- 正規表現は「事故りやすい表現」に絞る。広げすぎると解説記事まで落ちて運用が回らなくなるので、否定先読みで一般語を逃がすのが実務上のコツ。
同じ仕組みは、価格・在庫・締切など「言い切ると危うい」データを扱うどのサイトにも移植できる。実際の運用サンプルとして、このゲートを通した記事だけが並ぶマンガセール速報を眺めると、どの記事にも確認日と出典が付いているのが分かるはずだ。