やりたいこと
Qiitaでストックした記事がどんどん溜まっていくのに、振り返る機会がない。
そこで、Pythonスクリプトで Qiitaのストック記事を自動取得 し、ObsidianのVaultにMarkdownファイルとして保存 する仕組みを作りました。
タスクスケジューラで定期実行すれば、勝手にリーディングリストが育っていきます。
完成イメージ
実行するとこんなログが流れて、
=== Qiita ストック取得中... ===
[SAVED] ClaudCode入門
[SAVED] Python便利帖
... 完了: 24 件処理
Obsidianにはこんなファイルが自動生成されます。
ReadingList/
└── Qiita/
├── 2026-03-16_ClaudCode入門.md
├── 2026-03-16_Python便利帖.md
└── ...
各ファイルにはYAMLフロントマターが付いているので、Dataviewプラグインで未読一覧を表示したり、タグでフィルタしたりもできます。
title: "Claudcode入門"
source: Qiita
url: https://qiita.com/xxx/items/yyy
author: "@someone"
tags: ["Claude"]
saved_at: 2026-03-16
status: unread
事前準備
-
Pythonのインストール
https://www.python.org/downloads/ からダウンロード。
インストール時に 「Add Python to PATH」にチェック を入れてください。 -
requestsライブラリのインストール
Copypip install requests
- Qiitaアクセストークンの取得
https://qiita.com/settings/applications にアクセス
「新しくトークンを発行する」をクリック
スコープは read_qiita のみにチェック
「発行する」→ 表示されたトークンをコピー(※二度と表示されません)
スクリプト
以下のファイルを作成してください。
"""
Qiitaストック → Obsidian ReadingList 自動取得スクリプト
"""
import os
import re
import json
import time
import requests
from datetime import datetime
from pathlib import Path
# ============================================================
# 設定
# ============================================================
# Obsidian Vault の ReadingList フォルダ(自分の環境に合わせて変更)
VAULT_PATH = Path(r"C:\Users\yourname\Documents\Obsidian Vault\ReadingList")
# Qiita
QIITA_USER_ID = "your_qiita_id" # ← 自分のQiitaユーザーID
QIITA_TOKEN = "your_qiita_token" # ← 取得したアクセストークン
# ============================================================
# ユーティリティ
# ============================================================
def sanitize_filename(name: str) -> str:
"""ファイル名に使えない文字を除去"""
name = re.sub(r'[\\/*?:"<>|]', "", name)
name = name.strip()
if len(name) > 100:
name = name[:100]
return name
def already_saved(folder: Path, url: str) -> bool:
"""同じURLの記事が既に保存されているかチェック"""
if not folder.exists():
return False
for f in folder.glob("*.md"):
try:
content = f.read_text(encoding="utf-8")
if url in content:
return True
except Exception:
continue
return False
def save_article(folder: Path, title: str, url: str, author: str,
tags: list, source: str):
"""Markdownファイルとして保存"""
folder.mkdir(parents=True, exist_ok=True)
if already_saved(folder, url):
print(f" [SKIP] 既に保存済み: {title}")
return
date_str = datetime.now().strftime("%Y-%m-%d")
safe_title = sanitize_filename(title)
filename = f"{date_str}_{safe_title}.md"
filepath = folder / filename
tags_yaml = json.dumps(tags, ensure_ascii=False) if tags else "[]"
tags_md = " ".join([f"#{t}" for t in tags]) if tags else ""
content = f"""---
title: "{title}"
source: {source}
url: {url}
author: "{author}"
tags: {tags_yaml}
saved_at: {date_str}
status: unread
---
# {title}
- **元記事**: [{source}]({url})
- **著者**: {author}
- **タグ**: {tags_md}
## メモ
"""
filepath.write_text(content, encoding="utf-8")
print(f" [SAVED] {title}")
# ============================================================
# Qiita ストック取得
# ============================================================
def fetch_qiita_stocks():
"""Qiita APIでストック記事を取得"""
print("\n=== Qiita ストック取得中... ===")
folder = VAULT_PATH / "Qiita"
headers = {}
if QIITA_TOKEN:
headers["Authorization"] = f"Bearer {QIITA_TOKEN}"
page = 1
total_saved = 0
while True:
url = f"https://qiita.com/api/v2/users/{QIITA_USER_ID}/stocks"
params = {"page": page, "per_page": 100}
try:
resp = requests.get(url, headers=headers, params=params)
resp.raise_for_status()
except requests.RequestException as e:
print(f" [ERROR] Qiita API エラー: {e}")
break
items = resp.json()
if not items:
break
for item in items:
title = item.get("title", "無題")
item_url = item.get("url", "")
author = item.get("user", {}).get("id", "unknown")
tags = [t.get("name", "") for t in item.get("tags", [])]
save_article(folder, title, item_url, f"@{author}", tags, "Qiita")
total_saved += 1
if len(items) < 100:
break
page += 1
time.sleep(1)
print(f" Qiita 完了: {total_saved} 件処理")
# ============================================================
# メイン
# ============================================================
def main():
print(f"実行時刻: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"保存先: {VAULT_PATH}")
VAULT_PATH.mkdir(parents=True, exist_ok=True)
(VAULT_PATH / "Qiita").mkdir(exist_ok=True)
fetch_qiita_stocks()
print("\n=== 完了! ===")
if __name__ == "__main__":
main()
使い方
手動実行
Copypython fetch_qiita_stocks.py
タスクスケジューラで毎日自動実行(Windows)
Windowsの検索で「タスクスケジューラ」を開く
「基本タスクの作成」→ 名前を「Qiitaストック取得」
トリガー → 「毎日」→ 好きな時間(例: 朝9:00)
操作 → 「プログラムの開始」→ python を指定、引数にスクリプトのフルパス
Obsidianで未読一覧を表示(Dataviewプラグイン)
Dataviewプラグインをインストールしておけば、以下のクエリで未読記事だけ一覧表示できます。
TABLE author, tags, saved_at
FROM "ReadingList/Qiita"
WHERE status = "unread"
SORT saved_at DESC
仕組み
使っているのは Qiita API v2 の GET /api/v2/users/:user_id/stocks エンドポイントです。認証トークンありだと1時間に1000リクエスト可能なので、よほどの大量ストックでない限り問題なく全件取得できます。
重複チェックはファイル内のURLで判定しているため、2回目以降の実行では新規ストック分だけが追加されます。
まとめ
「あとで読む」でストックした記事は、外に出してあげないとなかなか読みにいきません。
Obsidianに集約しておけば、Dataviewで一覧化もできるし、読んだらメモを書き足すこともできます。
ストックを腐らせている方はぜひ試してみてください。