🦴
Zenn Scrap 本文取得方法
公式APIがないため取得に手こずったが、探りながら発見した。
// 期待していた場所(存在しない)
scrap.bodyMarkdown
scrap.content
scrap.body
// 実際の場所
pageProps.comments[0].bodyHtml // HTML形式でレンダリング済み
実装
- 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;
}
- データ構造の解析
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; // これが本文!
}
- React側での表示
// Markdownとして扱う(間違い)
<ReactMarkdown>{scrap.bodyMarkdown}</ReactMarkdown>
// HTMLとして扱う(正解)
<div dangerouslySetInnerHTML={{ __html: scrap.bodyMarkdown }} />
発見に至るまでのプロセス
- APIの調査
const response = await fetch(`https://zenn.dev/api/scraps?username=${username}`);
- データ構造の網羅的調査
// 可能性のある全フィールドをチェック
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], // ← ここにあった!
};
- ログ駆動デバッグ
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: "<!-- 実際の本文はここ -->",
// ...
}
]
}
}
}
学び
-
APIとWebスクレイピングのハイブリッド戦略
公式API: メタデータ取得
スクレイピング: 本文取得
組み合わせることで完全なデータを取得 -
プラットフォーム固有の設計思想を理解する
Zennにおいて:
Scrap = コメントの集合体
最初のコメント = 本文
この設計思想を理解することで、データの在り処が推測できる
APIの世界では、ドキュメントが不完全だったり、実際の構造が予想と違うことがよくあるので、こういった探偵的なアプローチが重要。
Discussion