← ishizakahiroshi.com
設計判断 2026-08-07

「10,000PV突破おめでとう」が9割 bot だったので、開き直って公開APIを作った話

大量のリクエストが押し寄せるサーバーと、その脇で静かに開かれた公開データの窓口

Cloudflare から「月間 10,000 PV を突破しました」というメールが届きました。国別の内訳は米国 8,594 に対して日本 142。1 つの IP が全体の 58% を占めていて、その数がサイトの収録校数とほぼ一致していました。bot を除いた実訪問者は 24 時間で 12 人です。スクレイプは止められないので、開き直って公式にデータセットと名乗ることにしました。そこで「公開する 1 件ごとに出典 URL を必ず添える」というルールを 1 本入れたら、データの充足率が 67% しかないことが分かった、という話です。

Qiita で読む → note で読む →
記事の要約: bot によるリクエストの内訳から、公開データセット化とデータ充足率の是正までの流れ

記事全体の要約です。左が 24 時間のアクセスの正体、右が「公式に配る」と決めてから見つかったデータ品質の穴です。

この記事のテーマ

自分で作っている高校選びのサービス manabi-map の話です。アクセスの正体を割るときに User-Agent は見ていません。偽装が 1 行で済むものは判定材料にならないからです。代わりに使ったのは、割り当て(RDAP)と逆引き(PTR)という、詐称にコストがかかる 2 つでした。大手クローラーは逆引きの名前を正引きし直して元の IP に戻るかで検証できるようになっているので、裏返すと PTR を持たない IP はどの大手クローラーでもないと言い切れます。

止められないと分かってからの判断が本題です。データは CC BY-SA 4.0 なので、公式に配布物として宣言すれば再利用する側に帰属表示の義務が発生します。黙って抜かれ続けるより、出典付きで引用される方がいい。そう決めて「公開する各レコードは出典元の URL を必ず同梱する」という条件を置いたところ、出典が確認できない学校が 3 分の 1 あると判明しました。原因は 3 週間前の自分で、データ投入をブロックごとに手書きしていたせいで、後半のブロックほど項目が静かに落ちていました

24 時間のリクエスト 9.23k の内訳。大半がクローラーで、実人間に紐づくのは 200 前後

24 時間ぶんのリクエストを分解したものです。9.23k のうち、実際の人に紐づくのは 200 前後でした。

投入ブロックが進むほど公式サイト URL の充足率が 100% から 0% へ落ちていくグラフ

ブロックが進むほど項目が脱落していきます。しかも項目ごとに崩れ始める時期が違い、列リストが手書きだったことを裏づけていました。

学校の設置者ごとに参照すべき公式カタログが変わることを示す対応表

取りこぼしの正体は地理ではなく所管でした。県立高校しか載っていないカタログを何回舐めても、市立高校は 1 校も増えません。

生成側の 3 ゲートと検証側の 4 throw を対比した図

生成側は「載せてよいものだけ載せる」、検証側は「載ってはいけないものが載っていたら落とす」で向きが逆です。片方だけだと、実装を書き換えたときに静かに緩みます。

結局どうなったか

充足率は 67% から 99.8% まで戻り、公開 API は本番で動いています。全都道府県が /api/v1/schools.json、県別が /api/v1/schools/{prefecture}.json で、説明ページは manabi-map.app/data/ です。1 レコードごとに出典 URL が同梱されているので、受け取った側がその 1 件だけを見て元の資料まで辿れます。CC BY-SA 4.0 なので、帰属表示さえしてもらえれば自由に使えます。

一番刺さったのは「文章で書いた防御は守られない」でした。手順書には「公立と私立の両方を押さえる」と書いてあり、設計メモには「1 県でも全欠落したら停止する」と書いてありました。どちらも守られていませんでした。読む人が読む前提で書かれた防御は、実行時に飛ばされます。機械で落ちる形にして初めて防御になります。