本記事はFIXERが提供する「cloud.config Tech Blog」に掲載された「技術ニュースを毎朝スマホで流し読みできる自分専用サイトを作った話」を再編集したものです。
こんにちは、中島です。
こちらの記事(Claude Codeで自分好みの朝刊が届く仕組みを作った) で Claude Code + Pythonを使って毎朝の技術ニュースを自動収集する仕組みが紹介されていて、めちゃくちゃ良かったので自分でも作りました。
元記事はMarkdownテーブルに出力する形でしたが、僕は朝の電車でスマホで読みたかったのでWebアプリにして、さらにスコアリングやUIの設計書を書いてガチ作り込んだりもしました。
今回作ったもの
システム構成
| 層 | 技術 | 役割 |
| 収集 | Python 3標準ライブラリのみ | 7ソースからRSS / HTMLを取得しパース |
| 保存 | git コミット済み static JSON | 日付単位 data/YYYY-MM-DD.json |
| 配信 | Vite 5 + React 18 + TypeScript + Tailwind 3 | SPA、`vite build` が 441 ms |
| スケジュール | GitHub Actions の schedule cron | 毎朝6時JSTに収集 |
| ホスト | Vercel無料枠 | git pushで自動デプロイ |
Python側は外部ライブラリゼロでurllibとxml.etree.ElementTreeだけです。
SPA側もfetch 1本で動くので、ルーティングも状態管理ライブラリも導入していません。
元記事をベースとした部分、変更点
元記事の「ソースをGroup A/B/Cに分けてブロック対策する」構造や、Reddit / YouTube は Python スクリプトに逃がすアイデアはそのまま使わせてもらっています。
変えたのはこの辺です。
| 元記事 | 自分の実装 | 理由 |
| Markdown 出力 | JSON → React SPA | スマホで読みたかった |
| launchd で毎朝実行 | GitHub Actions cron | マシン起動に依存したくなかった |
| Claude Code の WebFetch で取得 | Python 標準ライブラリのみ | GHA で Claude Code は実行できない |
| 6 ソース | ソース + Top 10 + あとで読む7 | Google News 追加、スコアリングで Top 抽出 |
Next.jsは使っていません。更新は1日1回で動的コンテンツもないので、Viteのbuildで十分です。
テストは先に書いて、実装と分けた
Claude Codeに実装を任せるときに怖いのは、テストを通すためだけの嘘実装ができてしまうことです。
ダミーデータで埋めたり、assertを弱めたり、skipを入れたりするパターン。
対策として、テスト設計と実装担当を物理的に分けて、最後にクロスレビューするフローにしています。
1. テスト設計担当がテストとスケルトンを先に書く
2. 僕がテスト観点をレビュー
3. 実装担当がテスト全 PASS するまで書く。テストファイルには触らない
4. テスト設計担当がクロスレビュー。テストファイルのmtimeが自分の作業時刻で止まっているかを確認する
Python側99テスト、Web側 35テスト、合計134テストで固めました。
mtimeベースの検証は地味ですが、テスト詐欺の検出にはこれで十分です。
業務でレビューが形式的になりやすい体制でも、テストを書く人と実装する人を分けるだけで品質が変わると思います。
つまずいた箇所 3 点
1つ目:YouTubeのchannel IDが本番で404
元記事にもあった YouTube RSSフィードの収集で、ハードコードしたchannel IDが404を返してきました。
最初は1 channel 失敗でソース全体が死ぬ構造だったので、channel単位でtry/exceptを貼って、失敗したらstderrにログ吐いて次へ進む形に直しました。
外部APIや外部feedは単品で死ぬ前提で設計するのが鉄則です。
2つ目:JSTの日付が1日ずれた
getTimezoneOffset()で日付を組み立てたところJSTブラウザで1日ずれました。
Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Tokyo' }) に差し替えて解消しました。
toISOString().slice(0, 10) はUTC基準なので23時台に前日を拾ってしまいます。
業務コードでも「今日の YYYY-MM-DD が欲しい」ときはIntl.DateTimeFormatを第一候補にしておくと安全です。
3つ目:Vercelのモノレポ対応
リポジトリルートにPython、web/ にViteプロジェクトという構成だとVercelのデフォルト検出でbuildが落ちます。
vercel.jsonでinstallCommandとbuildCommandをcd web && ...にして、outputDirectoryをweb/distに指定するだけで直りました。
ここから拡張:スコアリングとUIをガチで作り込んでみた
毎朝使い始めて、7ソース200件超の記事が並ぶと『全部見る気にならないこと』に気づきました。
「自分に関係ある記事だけ見たい」ので拡張を入れました。
作り込みポイント①:スコアリング + Top 10
各ソースのスコア(Qiita の likes_count、HN の points、はてブの bookmark 数)をソースごとにmin-max正規化して0-100に揃え、全ソース横断でTop 10を抽出。
UI には Top タブとスコアバッジを追加しました。
作り込みポイント②:興味キーワードブースト
自分の興味あるキーワード(Claude Code, React, AI, LLM, TypeScriptなど18語)にマッチしたらスコアに +15。
Top 10のうち6件が自分の興味にマッチするようになりました。
作り込みポイント③:設計書を書いてからリデザイン
800行のdocs/design.mdを書いて、Google News / Zenn / Daily.dev / Hacker News の4サイトを分析してからリデザインしました。
カラーシステム・タイポグラフィ・a11y・ダークモード方針を決めてから実装に入る形です。
設計書を書くのは楽しかったんですが、「書いた通りになっているか」を Puppeteer で検証するのが一番しんどかったです。
作り込みポイント④:「あとで読む」
朝の電車で気になる記事をlocalStorageにマークしておく機能。全ソース横断で「あとで」タブに集約されます。
業務でも使えそうなポイント
1. 動的コンテンツが不要なら、静的JSON + SPAで十分か検討する
2. 収集層と配信層を分離しておくと差し替えやすい
3. テスト設計と実装担当を分けるだけでテスト詐欺対策になる
4. 外部API / feedは単品で死ぬ前提でtry/exceptを入れる
5. デプロイが「git push で1発」になる構成を選ぶ
まとめ
元記事の「自分好みの朝刊」というコンセプトをもらって、スマホで読めるWebアプリにして、スコアリングで情報量を絞って、設計書書いてUIをちゃんと作りました。
200件の記事を全部並べても誰も読まないので、Top 10 + 興味ブーストで「自分に関係ある10件」に絞って、それをスマホで気持ちよく読めるUIにする。ここまでやって初めて毎朝開くサイトになった感覚があります。
テストは最終的に85件まで増えて全PASS。Claude Codeにテスト設計と実装を分けて書かせるフローは、業務のプロトタイピングでも使えると思います。
参考サイト:
・Claude Codeで自分好みの朝刊が届く仕組みを作った
Kanei Nakashima/FIXER
2024年入社です。中嶋ではなく中島。医療DXやってますん。
本記事はアフィリエイトプログラムによる収益を得ている場合があります


この連載の記事
-
TECH
「SOSの出し方を知ろう」 新卒入社から1年、学んだことを振り返る -
TECH
MobSF(Mobile Security Framework)でできること、動かない理由 -
TECH
3週間の自動テストが半日に! Playwrightの使い方の基本 -
TECH
Next.jsで静的テスト環境を構築し、GitHub Actionsで自動化してみた -
TECH
クイズ:正規表現で「0~255」のすべてにマッチするのはどれ? -
TECH
ミニPCサーバーにFluxを導入、GitOpsで自動デプロイする方法 -
TECH
アプリ開発の手戻りを防ぐ 「入力チェック(バリデーション)」の設計方法 -
TECH
Webアプリを使いやすく! 「入力チェック(バリデーション)」の正しい考え方 -
TECH
機械科卒・ITエンジニア就職から一年、やって良かったこと -
TECH
Chrome DevTools MCPとは? Claude Codeとの連携でWebアプリ開発体験が劇的に変わった -
TECH
Terraformのバージョン管理ツール、古いtfenvからtenvへの移行 - この連載の一覧へ



