前回(第3弾)では、公式APIが突然死んだことをきっかけに、HTMLスクレイピングへ転換した話を書いた。投稿の自動化は完成した。だが、記事を書いて投稿するだけでは片手落ちだと気づいた。
発信だけして、他者の記事を読まないのはマナー違反だ。
note.comはSNSだ。フィードに流れてくる他のクリエイターの記事を読み、いいねを送り、コメントで感想を伝える。それが循環を生む。しかし手作業でこれをやると時間がかかる。ならば自動化するしかない。
1. 作るもの:engageコマンド
設計はシンプルだ。
フェーズ1(fetch):
新着記事を取得 → AIがコメント草案を生成 → YAMLキューに保存
フェーズ2(post):
ユーザーがYAMLを確認・編集 → いいね+コメントを実行
人間が草案をチェックする工程を必ず挟む。AIが勝手にコメントを垂れ流す設計にはしない。 これは信条だ。
2. 新着記事フィードの発見
最初の課題は「どの記事にいいねするか」だ。フォローしているクリエイターに限定する案もあったが、それでは出会いがない。note.com全体の新着を取りたい。
FirefoxのDevToolsを開き、note.comのトップページを眺める。ネットワークタブに流れるリクエストの中に、見覚えのないエンドポイントがあった。
GET /api/v3/notes?page=1&per=20
叩いてみると、全クリエイターの新着記事が返ってくる。しかもbodyフィールドにプレーンテキストの本文まで含まれている。スクレイピング不要だ。
{
"data": [
{
"type": "TextNote",
"key": "n8699e4e63db3",
"name": "記事タイトル",
"body": "本文テキスト...",
"user": { "urlname": "someuser", "nickname": "クリエイター名" }
}
],
"next_page": 2
}
--page Nフラグでページを指定すれば、毎回違う記事が取れる。10件ずつ処理するのが今のやり方だ。
3. いいねAPIの発見——v3、しかもIDではなくkeyで叩く
いいねのAPIは一筋縄ではいかなかった。最初に試したのはこれだ。
POST /api/v1/notes/{数値ID}/likes → 403 Forbidden
403。自分の記事への自己いいねが禁止されているのかと思った。違った。エンドポイント自体が間違っていた。
正しいエンドポイントはPythonのPlaywrightでブラウザの実際の通信を記録して発見した。
POST /api/v3/notes/{key}/likes
v3で、数値IDではなく英数字のkey(n58da77ec9658のような文字列)を使う。レスポンスはHTTP 201。これで動いた。
4. コメントAPIの発見——構造体JSONが必要だった
コメントも同じく罠があった。当初の実装はこうだ。
payload := map[string]string{"comment": "コメント本文"}
POST /api/v1/notes/{数値ID}/comments
実行すると全記事で「この記事はコメント機能が無効になっています」という403が返ってくる。10件全部。
嘘だ、と思った。実際にnote.comでコメント欄を開いて投稿ボタンを押し、DevToolsでリクエストをキャプチャした。
POST /api/v3/notes/{key}/note_comments
{
"comment": {
"type": "root",
"children": [{
"type": "element",
"tag_name": "p",
"children": [{"type": "text", "value": "コメント本文"}]
}]
},
"acknowledgement": false
}
エンドポイントもペイロードも全然違った。
- v1ではなくv3
- 数値IDではなくkey
- プレーンな文字列ではなくAST(抽象構文木)形式のJSON
note.comはリッチテキストエディタのデータ構造をそのままAPIに渡す設計になっている。テキストノードを段落要素でラップし、それをrootで包む。
5. レート制限の壁
コメントAPIが動き始めると、すぐに次の壁にぶつかった。
HTTP 429
{"error": {"message": "上限に達したためご利用できません。しばらくお待ちいただいた後、再度お試しください。", "type": "too_many_requests"}}
1セッションで約9件コメントした直後に、次のバッチで全件429になった。コメントにはレート制限がある。いいねには今のところ制限は見当たらない。
対策は2つ。
コメント間に3秒のsleepを入れる
バッチ間は5分以上空ける
6. キューファイルの設計
フェーズ1で生成されるYAMLはこういう構造だ。
creator: random
fetched_at: "2026-07-12T19:34:29+09:00"
articles:
- key: n8699e4e63db3
title: "記事タイトル"
url: "https://note.com/someuser/n/n8699e4e63db3"
summary: "本文の先頭200文字..."
comment_draft: "AIが生成したコメント草案(100字以内)"
action: like # like | comment | skip
liked: false
comment_id: 0
actionフィールドを人間が編集してから--postを実行する。commentにした記事だけコメントが投稿される。草案が気に入らなければ書き直す。Nullと書けばいいねのみ。
コメント草案の生成にはOpenAI gpt-4o-miniを使っている。プロンプトは「記事タイトルと本文200文字から、日本語で100字以内のコメントを生成せよ」というだけだ。
7. 実際に動かしてみて
# 新着10件を取得(page=2から)
go run ./src/ engage --limit 10 --page 2
# キューを確認・編集してから投稿
go run ./src/ engage --post engage/queue-20260712-193449.yaml
いいね10件は一瞬で完了する。コメントは草案確認があるぶん、投稿まで少し手がかかるが、それでいい。完全無人化より、人間が一度読む設計のほうが誠実だ。
振り返り
今回の教訓:
- DevToolsは最強の仕様書 — v1とv3、IDとkey、単純文字列とAST、全部DevToolsが教えてくれた
- 「403=禁止」と思うな — エンドポイントが間違っているときも403が返る
- レート制限は実験して初めてわかる — ドキュメントには書いていない
次は、フォロー中クリエイターの新着を優先取得する機能と、コメント済み記事の重複スキップを実装したい。
コメントや知見の共有はコメント欄へどうぞ。
