前回の記事で紹介した「RSS Portal」を実際に運用してみたところ、いくつかの課題が見つかりました。今回は、それらの改善と GitHubでのOSS公開 についてまとめます。

RSS×AIで情報収集を自動化!採点&学習する自分好みの情報ポータルの作り方 RSSフィードをAIが自動でスコアリングし、興味のある記事だけを表示する情報ポータルシステムの作り方。FastAPI + OpenRouter + WordPressで構築し、Like/Dislikeフィードバックで学習する仕組みを解説。  続きを読む

    運用して見つかった課題

    実際に数日間運用してみると、以下の問題が発生しました。

    問題 原因
    2023年の古い記事が表示される RSSフィードに残っている古い記事も取得していた
    「Zenn」の記事がズラッと並ぶ フィード順に取得し、同一フィードの制限がなかった
    「Qiita」が表示されない URLが http:// のままでフィードが取得できていなかった

    特に「Zennのトレンド」は記事数が多く、それだけで表示枠を埋めてしまう状態でした。


    改善した内容

    1. 古い記事を除外(14日制限)

    rss_fetcher.py に、公開日が14日以上前の記事をスキップする処理を追加しました。

    from datetime import datetime, timedelta
    
    def is_article_too_old(published_at, days_limit=14):
        """記事が古すぎるかチェック"""
        if not published_at:
            return False
        
        pub_date = datetime.fromisoformat(published_at)
        cutoff_date = datetime.now() - timedelta(days=days_limit)
        return pub_date < cutoff_date
    
    # fetch_single_feed() 内で使用
    for entry in feed.entries:
        published_at = parse_published_date(entry)
        
        # 14日以上前の記事はスキップ
        if is_article_too_old(published_at):
            continue
    

    2. 同一フィードからの表示制限

    database.pyget_scored_articles() に、同一フィードから最大10件までという制限を追加しました。

    from collections import defaultdict
    
    def get_scored_articles(min_score=1, limit=100, max_per_feed=10):
        """スコアリング済みの記事を取得(同一フィード制限付き)"""
        
        # まず全件取得
        all_articles = fetch_all_scored_articles(min_score)
        
        # 同一フィードからの記事数を制限
        feed_count = defaultdict(int)
        filtered_articles = []
        
        for article in all_articles:
            feed_name = article['feed_name']
            
            if feed_count[feed_name] < max_per_feed:
                filtered_articles.append(article)
                feed_count[feed_name] += 1
            
            if len(filtered_articles) >= limit:
                break
        
        return filtered_articles
    

    3. 表示順を「投稿日順」に変更

    以前は fetched_at(取得日時)でソートしていたため、リライトされた古い記事が上位に来ることがありました。

    -- 変更前
    ORDER BY a.fetched_at DESC, a.ai_score DESC
    
    -- 変更後
    ORDER BY a.published_at DESC, a.ai_score DESC
    

    これにより、投稿日が新しい順 → 同日内はスコア順 という自然な並びになりました。

    4. フィードの整理と海外サイト追加

    実際に参考にしているサイトに厳選しました。

    整理後: 約20フィード(厳選 + 海外サイト追加)

    追加した海外フィード:

    サイト 特徴
    CSS-Tricks フロントエンド技術の定番
    Codrops インタラクティブなUI/UXの実装例
    Smashing Magazine Web制作全般の深掘り記事
    Ahmad Shadeed CSS実装パターンの詳細解説
    Josh W Comeau CSS/Reactのインタラクティブな解説

    海外サイトの記事も、AIが日本語で要約してくれるので問題なく読めます。


    設定値のチューニング

    config.py の設定を調整しました。

    設定項目 変更前 変更後 理由
    MAX_ARTICLES_PER_FETCH 100 2000 全フィードから取得するため
    MAX_ITEMS_PER_FEED 20 100 フィルタリング前に十分な母数を確保
    MAX_DISPLAY_PER_FEED なし 10 同一フィードの偏りを防止
    ARTICLE_RETENTION_DAYS 14 14 維持

    改善後の結果

    改善後のスクリーンショット

    • 日付順に並ぶな2026-01-31 → 01-30 → 01-29...
    • フィードが分散なZenn、Qiita、コリス、npaka など様々なソースから表示
    • 古い記事なしな14日以内の記事のみ

    GitHubで公開しました

    RSS Portal をOSSとして公開しました。

    GitHub: sarap422/api-rss-portal

    含まれるファイル

    api-rss-portal/
    ├── api/rss-portal/
    │   ├── main.py           # FastAPI本体
    │   ├── config.py         # 設定(APIキー、興味分野)
    │   ├── rss_fetcher.py    # RSS取得
    │   ├── ai_scorer.py      # AIスコアリング
    │   ├── database.py       # SQLite操作
    │   ├── json_output.py    # JSON出力
    │   ├── cron_job.py       # 定期実行
    │   └── data/feeds.opml   # サンプルフィード
    ├── public_html/api/rss-portal/
    │   └── .htaccess         # プロキシ設定
    ├── rss-portal.php         # WordPress組み込み用
    └── README.md
    

    使い方

    1. ファイルをサーバーに配置
    2. config.py でAPIキーと興味分野を設定
    3. data/feeds.opml にRSSフィードを登録
    4. Cronで定期実行を設定
    5. WordPressに rss-portal.php を組み込み

    詳細はREADMEを参照してください。

    興味のある方はGitHubからクローンして、ぜひ自分好みにカスタマイズしてみてください。


    関連記事

    RSS×AIで情報収集を自動化!採点&学習する自分好みの情報ポータルの作り方 RSSフィードをAIが自動でスコアリングし、興味のある記事だけを表示する情報ポータルシステムの作り方。FastAPI + OpenRouter + WordPressで構築し、クリック, Like/Dislikeフィードバックで学習する仕組みを解説。  続きを読む

    関連リンク