はじめに
以前公開した記事「[GAS] Qiita記事の統計(PV・いいね・ストック)を全自動で可視化するGASを全力で作ってみた!」では、多くの方に閲覧いただき、ありがとうございました。
執筆のモチベーションを客観的な数字で支える――。このコンセプトを長期に渡り支えるため、運用をより盤石にした 「v2.2(完成版)」 へと進化させました。
今回のアップデートでは、単なる機能追加にとどまらず、設計思想を変え、Qiita, スプレッドシートにNotionを加えて、各要素の役割の再定義を実施しました。
公開時の版に不具合があったため、現在V2.4になっています
ダウンロードなどされた方はお手元のソースをご確認ください
TL;DR
- 役割の再定義: スプレッドシートを「差分キャッシュ」とし、Notion を「閲覧用ダッシュボード」として分離。NotionのWeb公開機能により、多彩な表現、マルチデバイスで閲覧可能、URLによる手軽な閲覧環境を実現
- 極限の高速化: ハッシュによる差分検知キャッシュにより、書き込み負荷を90%削減。スプレッドシート内への Notion ID (UUID) キャッシュと合わせ、大容量データでも快適な同期を実現
- 自動運用: 削除済み記事を検知し、スプレッドシートへ履歴としてアーカイブ([DELETED]マーク付与)。同時に Notion 側の表示を自動クリーンアップ(アーカイブ機能利用)する仕組みを実装
第1章:定期的かつ長期的な投稿を前提とした「設計の見直し」
長期運用を盤石にするための課題は、大きく分けて2つの方向にありました。
1. データ管理層(スプレッドシート)の効率化
- 更新コストの最適化: 記事数が増えるほど「何も変わっていない行」への書き込み負荷が累積し、GAS の実行時間を圧迫する
- ライフサイクル管理: Qiita 側で削除・非公開になった記事の状態を、自動で検知して「アーカイブ」として同期する仕組み
2. 閲覧層(表示環境)の柔軟な拡張
- デバイスを問わないアクセスの実現: 外出先やモバイルから、スプレッドシートを開く手間なく、URL 一つで直感的に状況を確認したい
これらの課題を解決するため、今回の v2.2 では 「スプレッドシートを『高速なキャッシュ』へと役割変更させつつ、閲覧用ダッシュボードとして Notion を新たに導入する」 という、ツール間の役割分担の再定義を行いました。
これらは数件の記事なら問題ありませんが、長期投稿によって記事数が増えた場合を見据えると、今のうちに改善しておくべき重要なポイントでした。
第2章:設計転換 ―― スプレッドシートを「キャッシュ」として使用する
v2.2 では、データの管理思想を 「ホットスポット指向の更新」 と 「ツール別の役割分担」 へとシフトしました。
更新フローの視認化
ハッシュによる差分検知と、キャッシュされた内部ID(UUID)による高速なNotion更新のフローを以下に示します。
1. スプレッドシート:ハッシュによる差分更新
各記事の「更新日時+統計値(ビュー、いいね、ストック数)」をハッシュ化して管理。APIから取得した最新のハッシュ値と比較し、「実際に変動があった記事(ホットスポット)」だけをシートに書き込む ようにしました。これにより、1000記事規模でも書き込み負荷を 90% 以上削減しました。
2. Notion:UUIDキャッシュによる高速同期
Notion API を用いたレコード更新には、そのページ固有の内部 ID(UUID)の指定が必須となります。以前は毎回 URL で検索して ID を特定していましたが、本バージョンではこれをスプレッドシート側にキャッシュし、直接 ID で更新(PATCH)するようにしました。
-
高速化: キャッシュされた ID で直接
PATCHを叩くため、更新時の「URL検索リクエスト」が ゼロ になります - 自動修復(Healing): スプレッドシートに ID がない場合や、新規に記事を追加した場合は、自動的に Notion を検索・作成して ID を補完します
3. モジュール化とオンオフ機能 (NEW)
「今は Notion は使わず、スプレッドシートだけで管理したい」というニーズに応えるため、一括制御フラグを導入しました。コード冒頭の ENABLE_NOTION_SYNC を切り替えるだけで、Notion 連携を完全に切り離せます。
4. データ・ライフサイクル管理 (NEW)
数年にわたる長期運用でもスプレッドシートが重くならないよう、データの保存期間に「階層化」を導入しました。
- デイリー履歴の 60 日制限: 毎日の合計値を記録する「推移データ」シートは、常に 直近 60 日分 に自動スリム化されます。これにより、シートの肥大化による GAS のタイムアウトを防ぎます
- 月次集計による永続化: 60 日の範囲外になるデータも、「月次集計」シートに月ごとのサマリー(月末時点の数値)として自動で書き込まれ、永続化 されます。長期的な成長曲線は、この軽量なシートでいつでも振り返ることが可能です
第3章:視認性の追求(スプレッドシート UI)
「今、どの記事が伸びているのか」を一瞬で、かつ正確に把握するためのアップデートを施しました。
-
前回比の自動リセット (NEW): 実行のたびに前回比を一旦
-(変動なし)にリセットし、今回の実行で実際に変動があった記事だけ に+1といった数値が表示されるようにしました。これにより、古い数値が残り続ける混乱を回避しています - スマート・ハイライトの安定化: ビュー数が実際に増加した場合のみ緑色に着色するロジックをより厳密にしました。前回比のリセットと合わせ、視覚的に「今、何が起きているか」が浮き上がる構成になっています
第4章:Notion API 同期の心臓部
以下が、ハッシュ比較、ID キャッシュ、およびオンオフ制御を統合した同期ロジックです。
// 5. Notion 同期 (フラグが有効な場合のみ実行)
if (ENABLE_NOTION_SYNC) {
// 変動があった記事、または NotionID が未取得の記事を同期対象にする
stats.filter(s => s.hasUpdate || !s.notionId).forEach(s => {
let pageId = s.notionId;
// IDキャッシュがない場合のみ検索を実行(自己修復)
if (!pageId) {
pageId = findNotionPageIdByUrl(s.url);
}
const properties = { /* 統計データ等の詰め込み */ };
if (pageId) {
// 直接更新(高速!)
updateNotionPage(pageId, properties);
} else {
// 新規作成
s.notionId = createNotionPage(properties);
}
});
// 消失した記事は Notion 側の表示を整理(アーカイブ)し、クリーンさを保つ
missingArticles.forEach(m => archiveNotionPage(m.notionId));
}
第5章:Notion連携のセットアップ
Step 1: スクリプトプロパティの設定
スプレッドシートの「スクリプト プロパティ」に以下を設定します:
-
QIITA_TOKEN: Qiita個人用アクセストークン -
NOTION_TOKEN: Notion内部インテグレーショントークン -
NOTION_DATABASE_ID: NotionデータベースID
Step 2: Notion データベースの構成
以下のプロパティを正確な名称(大文字小文字・スペースに注意!)と型で作成してください:
-
Name(タイトル) -
URL(URL) -
Views,Likes,Stocks(数値) -
Tags(マルチセレクト) -
Created(日付)※Qiita記事の作成日用 -
Updated(日付)※Notion側の最終更新日時用
スクリーンショットです
おわりに ―― 観測を「空気」にする
この v2.2 の完成により、統計の可視化は完全に「空気のような存在」になります。
観測のコストを最小化し、執筆という「創造」に集中できる環境。技術で運用を飼い慣らす。そんなエンジニアらしい解決策としての v2.2 を、ぜひ体験してみてください。
ソースコード全文(v2.2 完成版)
下記リンクより GitHub Gist で直接ご確認いただけます。
QiitaStatsV2.2 完成版 (GitHub Gist)
不具合に対応したので、V2.4になっています
ADR (Architecture Decision Record):
- 情報の二層キャッシュ構造: スプレッドシートを Notion 更新用の ID キャッシュ層として活用。URL 検索リクエストを排除し、処理速度を極限まで向上
- ホットスポット更新ロジック: 更新日時と統計値のハッシュ比較により、数値に変動のあった記事のみを書き換え対象とする
- 動的サマリー管理: シート最下部の「総計」行をプログラムが自動検知・更新。記事数変動にかかわらず常に正しい合計値を維持
- データ・ライフサイクル: デイリー履歴を直近 60 日間に制限し軽量化しつつ、月次の締め数値を「月次集計」シートに永続化
- トグル式モジュール制御: Notion 連携を単一フラグ
ENABLE_NOTION_SYNCで一括オンオフ可能にし、依存関係を疎結合に維持

