3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

意識を高くするためにテックニュースをGitHub Issueに登録するシステムを作った

3
Posted at

通勤中だったり始業時間までの時間にはてブ見ながら情報収集するのがルーティンなんですが、休日とかはもっと情報収集したいなーと思ってたんです。
スタバとかでMacで見てたらなんかかっこいいかもだし

作りたかったもの

平日は6時に起きて7:30には会社にいるか、エニタイムにいるか、コメダ珈琲で小倉トースト食べてるかなので、毎朝6時に自動実行されているといいなと思いました。
20以上のテックブログやニュースサイトから記事を収集して、Claude api でジャンル分析してGithub Issueとして登録するシステムです。
収集するテックブログは永久保存版!エンジニア向け情報収集サイトをまとめてみた【定期更新】とAIKIDOのセキュリティブログとかそんなんです。

機能

  • 毎朝06:00に自動実行(Github Actions)
  • テックブログ・ニュースサイトから記事を収集
  • ジャンルごとにGithub Issueを自動作成
  • 前日取得記事との差分チェック

ディレクトリ構成

  news-digest/
  ├── .github/
  │   └── workflows/
  │       └── news_digest.yml      # GitHub Actions設定
  ├── scripts/
  │   ├── main.py                  # メイン処理
  │   ├── fetch_news.py            # スクレイピング
  │   ├── summarize.py             # Claude API要約・分類
  │   ├── create_issues.py         # GitHub Issue作成
  │   └── utils.py                 # キャッシュ管理
  ├── cache/
  │   ├── .gitkeep
  │   └── articles_cache.json      # 前日比較用(自動生成)
  ├── .gitignore
  ├── requirements.txt
  └── README.md

システムアーキテクチャ

  ┌─────────────────────────────────────┐
  │   GitHub Actions (JST 06:00)        │
  └──────────────┬──────────────────────┘
                 │
                 ▼
  ┌─────────────────────────────────────┐
  │  1. fetch_news.py                   │
  │     - 各サイトをスクレイピング          │
  │     - BeautifulSoup4で記事抽出        │
  └──────────────┬──────────────────────┘
                 │
                 ▼
  ┌─────────────────────────────────────┐
  │  2. utils.py                        │
  │     - キャッシュと差分チェック          │
  │     - 新規記事のみ抽出                 │
  └──────────────┬──────────────────────┘
                 │
                 ▼
  ┌─────────────────────────────────────┐
  │  3. summarize.py                    │
  │     - Claude API (Haiku)で要約       │
  │     - ジャンル自動分類                 │
  └──────────────┬──────────────────────┘
                 │
                 ▼
  ┌─────────────────────────────────────┐
  │  4. create_issues.py                │
  │     - GitHub REST API               │
  │     - ジャンルごとにIssue作成          │
  └──────────────┬──────────────────────┘
                 │
                 ▼
  ┌─────────────────────────────────────┐
  │  5. キャッシュ更新 & Git Commit       │
  └─────────────────────────────────────┘

実行後のIssue

image.png

工夫したポイント

1. レート制限対策

短時間で大量にリクエストを送ろうとすると429 Too Many Requestsが起こるかもなと思ったのでスクレイピングを実装してます。 当たり前か...

2. Claude API 呼び出し間の待機

Claude Apiの無料プランは5 requests/minuteの制限があるっぽい(claude調べ)ので
1秒待機だと毎分最大60のリクエストだとワンチャン制限オーバーになる可能性があるので、5件毎に5秒待機すれば実質 50-60/mに抑えられると思って実装してます。

  def summarize_articles(articles: List[Dict[str, str]], batch_size: int = 5) -> List[Dict[str, str]]:
      summarized = []

      for i, article in enumerate(articles, 1):
          summarized_article = summarize_article(client, article)
          summarized.append(summarized_article)

          # 5件ごとに5秒待機
          if i % batch_size == 0 and i < len(articles):
              print(f"{batch_size}件処理完了、5秒待機...")
              time.sleep(5)  # ← バッチごとに長めの待機
          else:
              time.sleep(1)  # ← 通常は1秒待機

3. キャッシュ管理

毎日同じ記事をIssue化するともう見なくなるので重複しないように調整しました。
URLには記事の一意の識別子として考えて同じURLなら同じ記事とするように考えました。

具体例:
  【1日目】
  取得: 50件 → キャッシュ: 0件 → 新規: 50件
  → 50件がまとめられたIssue作成

  【2日目】
  取得: 55件(5件は昨日と同じ) → キャッシュ: 50件 → 新規: 5件
  → 5件分のIssue作成のみを行う

みたいな流れです。
キャッシュの管理はGitで行っていて
ストレージを使いたくなかったり、GitHub Actionで実行間のデータを永続的にできるから使ってます。

  def get_new_articles(fetched_articles: List[Dict[str, str]]) -> List[Dict[str, str]]:
      # 前回のキャッシュを読み込む
      cache = load_cache()  # {"https://...": {...}, ...}
      cached_urls: Set[str] = set(cache.keys())  # ← URLのセット

      # 新規記事のみ抽出(URLが存在しないものだけ)
      new_articles = [
          article for article in fetched_articles
          if article["url"] not in cached_urls  # ← O(1)の高速検索
      ]

      print(f"取得記事: {len(fetched_articles)}")
      print(f"キャッシュ済み: {len(cached_urls)}")
      print(f"新規記事: {len(new_articles)}")

      return new_articles

4. コスパ

  • Claude APIは従量課金制(proとかMAXなら普通に使えるの・・・?)ので極力無駄なリクエストはしたくなかったのでModelはClaude Haikuを採用してます
  • 重複記事を要約しても無駄なのでキャッシュして既に要約済みならClaude Apiは呼ばないようにしています。
def main():
      # 1. ニュース収集
      all_articles = fetch_all_news()  # 50件取得

      # 2. 新規記事のみ抽出
      new_articles = get_new_articles(all_articles)  # 5件のみ新規

      if not new_articles:
          print("新規記事がありません。処理を終了します。")
          return  # ← Claude APIを一切呼ばない

      # 3. Claude APIで要約(5件だけ)
      summarized = summarize_articles(new_articles)

まとめ

Claude APIとGitHub Actionsで手軽に技術ニュースの自動収集システムを構築することができました。

今後の課題

  • GraphQL APIでより効率的なIssue作成
  • GitHub Projectsとの連携
  • Slackへの通知機能
  • 記事の重要度スコアリング
3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?