🦴

Zenn Scrap 本文取得方法

に公開

公式APIがないため取得に手こずったが、探りながら発見した。

//  期待していた場所(存在しない)
scrap.bodyMarkdown
scrap.content
scrap.body

//  実際の場所
pageProps.comments[0].bodyHtml  // HTML形式でレンダリング済み

実装

  1. HTMLの取得とパース
import { load } from 'cheerio';

async function fetchZennScrapContent(scrapUrl: string): Promise<string> {
  const response = await fetch(scrapUrl);
  const html = await response.text();
  
  // cheerioでHTMLをパース
  const $ = load(html);
  
  // __NEXT_DATA__からJSONデータを抽出
  const nextDataScript = $('script#__NEXT_DATA__').html();
  if (!nextDataScript) {
    throw new Error('__NEXT_DATA__ not found');
  }
  
  return nextDataScript;
}
  1. データ構造の解析
const nextData = JSON.parse(nextDataScript);
const pageProps = nextData.props.pageProps;

// Scrapの本文はcommentsの最初の要素にある
const comments = pageProps.comments;
if (comments.length > 0 && comments[0].bodyHtml) {
  return comments[0].bodyHtml; // これが本文!
}
  1. React側での表示
//  Markdownとして扱う(間違い)
<ReactMarkdown>{scrap.bodyMarkdown}</ReactMarkdown>

// HTMLとして扱う(正解)
<div dangerouslySetInnerHTML={{ __html: scrap.bodyMarkdown }} />

発見に至るまでのプロセス

  1. APIの調査
const response = await fetch(`https://zenn.dev/api/scraps?username=${username}`);
  1. データ構造の網羅的調査
// 可能性のある全フィールドをチェック
const candidates = {
  scrap_body: 'body' in scrap,
  scrap_content: 'content' in scrap,
  scrap_markdown: 'markdown' in scrap,
  comment_body: 'body' in comments[0],
  comment_bodyHtml: 'bodyHtml' in comments[0], // ← ここにあった!
};
  1. ログ駆動デバッグ
console.log('PageProps keys:', Object.keys(pageProps));
console.log('Comments count:', comments.length);
console.log('First comment keys:', Object.keys(comments[0]));
console.log('bodyHtml preview:', comments[0].bodyHtml?.substring(0, 200));

技術的知見

Zenn Scrapの内部構造

  • Scrapはコメントベースの仕組み
  • 最初のコメントが本文として扱われる
  • 本文はHTML形式で事前レンダリング済み
  • Markdownの生データは含まれない

データ構造

window.__NEXT_DATA__ = {
  props: {
    pageProps: {
      scrap: { /* メタデータのみ */ },
      comments: [
        {
          bodyHtml: "<!-- 実際の本文はここ -->",
          // ...
        }
      ]
    }
  }
}

学び

  1. APIとWebスクレイピングのハイブリッド戦略
    公式API: メタデータ取得
    スクレイピング: 本文取得
    組み合わせることで完全なデータを取得

  2. プラットフォーム固有の設計思想を理解する
    Zennにおいて:

Scrap = コメントの集合体
最初のコメント = 本文
この設計思想を理解することで、データの在り処が推測できる

APIの世界では、ドキュメントが不完全だったり、実際の構造が予想と違うことがよくあるので、こういった探偵的なアプローチが重要。

Discussion