書籍OCRパイプライン構築とnote.com Chrome拡張の内部API解析
専門書287ページをOCRでMarkdownに変換してDBに流し込むパイプラインを組んだ。その傍らでnote.comの内部APIをChrome DevTools MCPで傍受し、下書き保存が201を返すまで叩き続けた。2つのプロジェクトが交互に手を動かした一日。
1. 書籍OCR→DB格納パイプライン
yomitoku OCRでPDFをMarkdownに変換
専門書のPDF(287ページ)をyomitoku OCRに通してMarkdownに変換した。テキストだけでなく図表も画像として抽出され、220枚の画像が出てきた。
220枚→53枚: 画像の自動選別
220枚の中身を見ると、装飾画像やページ区切りのバナーが大量に混ざっていた。手で1枚ずつ消すのは現実的でないので、サイズと縦横比で自動分類するスクリプトを組んだ。
- 装飾画像 113件を削除: ファイルサイズが小さいチェックマークやアイコン類
- 横長バナー 54件を削除: 縦横比で判別したページ区切りの帯画像
- 実図 53件を残す: グラフ、表、フローチャートなど内容のある図
ユーザーが画像を見ながら「チェックマーク消して」「バナーも消して」と指示を出し、それをサイズ閾値と縦横比の条件に変換して一括処理した。
Turso DBに280チャンク格納
変換したMarkdownをチャンクに分割し、Turso DBに280チャンク格納した。Embedded Replicaの同期も実施して、ローカルからの読み取りが即座に反映される状態を確認した。
2. note.com Chrome拡張機能 & スキル開発
Chrome DevTools MCPで内部APIを調査
note.comの公開APIドキュメントは存在しないため、Chrome DevTools MCPでネットワークリクエストを傍受して内部APIの挙動を調べた。エディタ上で下書き保存ボタンを押したときのリクエストを捕まえるところから始めた。
下書き保存APIの発見
ネットワークログから下書き保存のエンドポイントを特定した。
POST /api/v1/text_notes → 201 Created
認証まわりで一つ重要な発見があった。XSRF-TOKENは不要で、X-Requested-With: XMLHttpRequest ヘッダーだけで認証が通る。Cookieのセッション情報と組み合わせれば、Chrome拡張からそのまま叩ける。
リクエストフォーマットの試行錯誤
最初は {note: {name: "...", body: "..."}} とラップして送ったが、400エラーが返ってきた。DevToolsで実際のリクエストボディを見直すと、トップレベルに name, body 等を直接配置する形式だった。
{
"name": "タイトル",
"body": "<p>本文HTML</p>",
"status": "draft"
}
ラップを外したら201が返り、下書きがnote.comのダッシュボードに現れた。
画像アップロードの壁: 3段階の試行錯誤
画像アップロードで3回壁にぶつかった。
試行1: アイキャッチ用エンドポイント
最初に見つけた /v1/image_upload/note_eyecatch を使った。アップロード自体は成功したが、画像が正方形にクロップされてしまう。アイキャッチ専用のリサイズ処理が走っていた。
試行2: 本文画像用のpresigned URL方式
本文中の画像は別の経路を使っていた。まず /v3/images/upload/presigned_post を叩いてS3の署名付きURLを取得し、そのURLにmultipart/form-dataで画像をPUTする2段階方式。
1. POST /v3/images/upload/presigned_post → S3署名付きURL + fields
2. POST S3のURL → 画像アップロード完了
試行3: CORS制限との戦い ステップ2のS3直接アップロードでCORS制限に引っかかった。Chrome拡張のcontent scriptからS3のオリジンへのリクエストがブロックされる。最終的にcurlコマンドを使ってアップロードする方式で回避した。ブラウザのJavaScript実行環境を経由しないので、CORSの制約を受けない。
Chrome拡張のリポジトリ分離
当初 chrome-extension-x リポジトリ内で開発していたが、note.com専用の機能が増えたため chrome-extension-note として独立リポジトリに分離し、GitHubにpushした。
note-draftスキルの定義
MDX記事をnote.comの下書きとして自動保存するスキルを、ユーザーレベル(~/.claude/skills/)で定義した。MDXファイルを読み取り、MarkdownをHTMLに変換し、note.com APIで下書き保存するまでの一連の流れを自動化した。
ドラフトロックの知見
note.comのエディタを開いたまま、APIから同じノートを更新しようとすると変更が反映されない。エディタ側がWebSocketでロックを取得しているらしく、APIからの書き込みが上書きされる。回避策として、既存ノートの更新ではなく毎回新規ノートを作成する方式にした。
今日の試行錯誤
| # | テーマ | 試したこと | 結果 | 気づき |
|---|---|---|---|---|
| 1 | 画像選別 | 220枚を目視確認 | 非現実的 | サイズ・縦横比で自動分類する方が速い |
| 2 | note認証 | XSRF-TOKEN取得を試行 | 不要だった | X-Requested-Withだけで通る |
| 3 | リクエスト形式 | {note: {...}} でラップ | 400エラー | トップレベルに直接配置が正解 |
| 4 | アイキャッチAPI | eyecatch用エンドポイントで本文画像UP | クロップされる | アイキャッチ専用のリサイズが走る |
| 5 | S3アップロード | content scriptからfetch | CORSブロック | curlで回避、ブラウザを経由しない |
| 6 | ドラフト更新 | 既存ノートをAPI更新 | エディタのロックで上書き | 新規ノート作成で回避 |
今日の学び
- 画像の自動選別はサイズと縦横比の2軸で大半をカバーできる。220枚を手で見るより、閾値を決めてスクリプトを回す方が速くて正確だった
- Chrome DevTools MCPでネットワークタブを覗き、実際のリクエスト/レスポンスをそのまま再現した。公式ドキュメントがないAPIの調査手段として定着しそう
- CORS制限はブラウザの制約なので、curlなどブラウザ外の手段を使えば迂回できる。Chrome拡張ではbackground scriptからのfetchやnative messagingも選択肢になる
- エディタを開いたままAPIを叩いたら変更が消えた。WebSocketのロック機構はAPIレスポンスには現れないので、実際に踏むまで気づけない