---
name: project-studystoa-phase1-og-image
description: STEP 1090 (2026-05-20) — studystoa Phase 1 (A)(B) 実装. OG image scraper module + edu-news fetcher image_url 拡張. live verify 26/30 (87%) 取得成功. 「rei-aios 研究継続 + ポータル徐々に構築」 framing 順守.
metadata: 
  node_type: memory
  type: project
  originSessionId: 39cf0664-2c03-43b3-8c69-38f902d4a019
---

# STEP 1090 — studystoa Phase 1 (A)(B) OG image scraper

## 背景

藤本さん 2026-05-20 studystoa ポータル構想:
- **rei-aios/dev 研究はそのまま継続**
- **ポータルサイトは徐々に構築**
- Microsoft Start / Apple News pattern (image-first card layout) 採用
- 既 rei-aios の edu-news fetcher (Inside Higher Ed + The Hechinger Report + HN Algolia) は **既稼働 35 items/日** だが image_url field 未存在 → studystoa の card layout に不適合

## 認知科学的根拠 (verify ✅)

- 画像認知速度 ~13ms vs 文字 ~250ms (MIT 2014 研究) = **20 倍速**
- Microsoft Start / Apple News / Yahoo / Smartnews / Google News すべて image-first card layout
- 藤本さん「人は文字より画像を先に見る」 framing は cognitive science 整合

## 実装 (Phase 1 A + B)

### (A) OG image scraping module — `scripts/research-radar/og-image-scraper.ts` 新規 195 行

- **依存なし** (regex + Node built-in `https` only, 既 stack pattern 整合)
- **Fallback chain**:
  1. `<meta property="og:image" content="...">`
  2. `<meta name="twitter:image" content="...">`
  3. `<meta name="twitter:image:src" content="...">` (older)
  4. `<link rel="image_src" href="...">` (oldest)
  5. null (graceful failure)
- **Functions**:
  - `extractOgImageFromHtml(html, baseUrl?)`: pure function (testable, no network)
  - `fetchOgImage(url)`: network fetch with timeout 5s + 2MB body limit + 1-hop redirect
  - `fetchOgImagesBatch(urls, {concurrency, gapMs})`: rate-limit friendly batch
  - `_resetOgImageCache()`: test-only cache reset
- **In-memory cache** (重複 fetch 回避, process-local)
- **User-Agent**: `Rei-AIOS-OgImageScraper/1.0` (法的 transparency)
- **Graceful failure** (never throws, null return)

### (B) edu-news fetcher 拡張 — `scripts/build-edu-news.ts`

- `NewsItem.imageUrl?: string | null` 追加 (schemaVersion 1 → 2)
- 全 source fetch 後に top-30 items に対して並列 OG fetch (concurrency=3, gap=300ms)
- `SKIP_OG_IMAGE=1` env で skip (offline / test 用)
- graceful: 画像取得不可 items は imageUrl=null retain (card 側で代替 graphic 判断)

## Live verification ✅

| URL | source | 時間 |
|---|---|---|
| Hechinger Report (proof-points-rapid-growth) | og:image | 143ms |
| Inside Higher Ed (5-questions-arthur-levine) | og:image | 799ms |
| GitHub (ossu/computer-science) | og:image | 937ms |

build-edu-news.ts 全体 run 結果:
- **26/30 (87%) items に OG image 取得成功**
- file size 8 KB → 16 KB
- 4/30 失敗 = 古い HN URL (一部 source は site 閉鎖) — graceful null retain

## test/step1090-og-image-scraper-test.ts (31/31 PASS)

| # | test | 確認内容 |
|---|---|---|
| 1-2 | og:image (property+content 順 / 逆順) | regex 両対応 |
| 3 | twitter:image fallback | og:image 不在時 |
| 4 | twitter:image:src fallback | older variant |
| 5 | link rel=image_src fallback | oldest |
| 6 | 全 fallback 不在 → null + source=none | graceful |
| 7 | 相対 URL → 絶対 URL resolve | baseUrl 経由 |
| 8 | baseUrl なし時 相対 URL retain | no crash |
| 9 | case-insensitive (OG:IMAGE / Og:Image) | 大文字小文字対応 |
| 10 | og:image:url variant | RDFa style |
| 11 | og:image > twitter:image > image_src 優先順位 | fallback chain test |
| 12 | real-world HTML (Inside Higher Ed 風) | actual site pattern |
| 13 | cache 機構 (null 結果も cache) | 重複 fetch 回避 |
| 14 | invalid input graceful | empty / non-URL string |
| 15 | fetchOgImagesBatch concurrency control | batch behavior |

## 法的 stance (本 STEP に明記)

- OG image / twitter:image / image_src は **元 source が公開を意図** している (SEO + SNS シェア用 meta tag)
- Yahoo / Smartnews / Apple News / Google News の慣行と整合 (**fair use 寄り**)
- 必ず:
  - 出典明示
  - 元 source への direct link
  - thumbnail size 維持
- source 側 robots.txt + Terms 確認 list は別途 docs (Phase 2 で整備)

## 「rei-aios 研究継続 + 徐々に」 framing 順守 ✅

- 本 STEP は rei-aios 内 research-radar 拡張で完結 (既 stack 整合)
- studystoa 本体 init は未着手 (別 turn で藤本さん指示後)
- Rei core mission (D-FUMT₈ / Lean 4 / 公理メタ) 影響なし

## 次の step (徐々に retain)

| step | 内容 | timeline |
|---|---|---|
| (A) | OG image scraping module 実装 | ✅ 完了 (本 STEP) |
| (B) | edu-news fetcher image_url 拡張 | ✅ 完了 (本 STEP) |
| (C) | studystoa Astro init + image-first card layout component (`C:\Users\user\studystoa\` 別 directory) | 別 turn 藤本さん指示後 |
| (D) | edu-news + research-radar data → studystoa bridge | (C) 後 |
| (E) | 法的 protocol: source 別 robots.txt + Terms 確認 list | (C) 後 |
| (F) | 他 research-radar fetcher (philosophy-bites / daily-nous 等) にも OG image 拡張 | (B) パターン流用 |
| (G) | Phase 2 グラフ化 module (画像取得不可記事用) | future |

## 関連 memory

- [[no-rush-publication]] — 「急がず ゆっくりと」 framing
- [[reference-studystoa-project-concept-2026]] — 未作成 (studystoa 構想 5 turn chat-Claude conv + strategic pivot retain 候補)
- [[auto-managed-file-conflict-protocol]] — edu-news/latest.json は generator script (build-edu-news) 経由のみ更新

## Honest scope

- 本 STEP は **studystoa 本体 init ではなく、 studystoa が利用する image asset preparation**
- 既 rei-aios の edu-news / research-radar が image-enabled になることで:
  - studystoa: HTTP fetch でこれら data を利用可能 (将来)
  - rei-aios: image asset 自体も Rei-AIOS site に表示可能 (将来 UI 拡張時)
- 法的 risk は practical low (Yahoo / Smartnews 慣行 follow)
- 26/30 (87%) 取得率は実用十分. 残 4 件は graceful null retain
- 「世界一級」 ambition は 5-10 年 timeline で retain, 本 STEP は Phase 1 minimal build block

## commit

`e89f2262` step(1090): OG image scraper + edu-news image_url 拡張 — studystoa Phase 1 (A)(B)
