# 「取れた」と読んでいたのは、ただの案内ページだった：3日間、商品が0件

> ポケモンセンターの取得が3日間ずっと商品0件だった。安い設定が返した2,410バイトの「JavaScriptを有効に」ページを、成功と読んでいた。判定を1か所に集めた記録。

- URL: https://koshinstudio.com/notes/blocked-page/
- 公開: 2026-10-08
- 書いた人: Koshin（https://koshinstudio.com/about/）
- サイト: Koshin Studio

  English version (https://koshinstudio.com/en/notes/blocked-page/)

トレカダッシュ 開発の記録 ／ 2026年10月

  iOS アプリ「トレカダッシュ」は、通販サイトの販売ページを定期的に見に行って、抽選や先着の情報を集めています。ある日、ポケモンセンターオンラインの取得が、3 日間ずっと、商品 0 件になっていたことが分かりました。エラーは出ていません。「取れた」と判定されていたからです。何が起きていたのか、コミットの記録で書きます。

  このサイトには、トレカダッシュの作品ページはまだありません。ここに書くのは、開発の記録です。

## 「取れた」の判定が、甘かった

  ページの取得は、外部のサービス（Scrape.do）を通して行っています。取得の設定には、安いものから高いものまであって、安い設定で取れなければ、強い設定に切り替える（昇格する）作りです（取得の費用の記事 (https://koshinstudio.com/notes/scrape-cost/)と同じ仕組みです）。

  この「取れたかどうか」の判定が、問題でした。ポケモンセンターのページは、安い設定で取ると、2,410 バイトの、「JavaScript を有効にしてください」という案内ページが返ってきます。商品の一覧ではなく、ただの案内です。ところが、これを「取れた」と読んで、成功と扱っていました。成功なら、強い設定への切り替えも行われません。

  こうして、9 月 4 日から 3 日間、取得は「成功」し続けて、商品は 0 件のまま、でした。

## 同じページを、3つの設定で叩いて測った

  原因を推測で決めないために、ポケモンセンターの検索ページを、取得の設定を変えて実際に取り直しました。安い設定は 1 クレジット、強い設定（super と render を併用）は 25 クレジットかかります。

    取得の設定 | 応答 | 中身

      安い設定（1 クレジット） | 200 | 2,410 バイト。「このサイトでは JavaScript を有効にする必要があります」だけ

      super + render（25 クレジット） | 200 | 278,322 バイト。商品 39 件

  応答コードはどちらも 200 です。通信の成功だけを見ていたら、この 2 つは区別できません。違いは中身の大きさだけで、約 115 倍ありました。

## 守りはあったのに、2 つとも寸前で通り抜けていた

  「取れたかどうか」を見る条件は、もともと何もなかったわけではありません。ページに次の 2 つの確認がありました。

- 本文が 2,000 文字未満なら、取れていないとみなす。案内ページは 2,410 バイトで、わずかに超えていました。

- 本文に「JavaScript が無効」という文言があれば、取れていないとみなす。実際の文言は「JavaScript を有効にする必要があります」で、この文言には一致しませんでした。

  どちらも、想定した形の少しだけ外側で、通り抜けています。確認の条件は、想定した 1 つの形にしか合っていませんでした。実際に来るページの形は、いくつもあります。

  通ってしまった後は、次のことが起きます。

- 強い設定に切り替え直さない。商品は 0 件のまま。

- 「安い設定で取れる」と覚えて、強い設定への切り替えを解除する。

- 以後ずっと 0 件。エラーが 1 件も出ないので、ログにも何も残らない。

## 3 日間の数字

    日時 | ポケモンセンターの取得件数

      9 月 4 日 12:45 まで | 10 件か 8 件（安定していた）

      9 月 4 日 12:45 から 9 月 7 日 | 0 件が 323 回連続。エラーは 1 件もなし

      直した後に手で実行 | 10 件に戻った

## 直し方

- 「取れた」の判定を、1 か所に集める。それまで、判定が複数の場所に散らばっていました。ブロックされたページかどうかを見る関数（scrapeDoResultLooksBlocked）を 1 本にして、全部がそれを使うようにしました。

- 通った設定を、覚える。一度成功した設定を覚えておき、次からは、そこから試します。これで、1 回の取得に使うクレジットが、36 から 25 に減りました。

## 新しい判定は、何を「取れていない」と読むか

  scrapeDoResultLooksBlocked は、次のどれかに当てはまると「取れていない」と答え、その理由の文字列も返します。理由はログに必ず残るので、あとから「なぜ強い設定に上がったか」が読めます。

- 本文が短すぎる。

- 順番待ちのページ（Queue-it）である。

- 中身が小さいのに、JavaScript を求める案内がある。

  3 つ目には、落とし穴があります。「JavaScript が無効になっています」という一文は、まっとうなページにも、noscript という要素として常に入っていることがあります。実際に、別の店（ヤマダ）のページで一度、取れているページを「取れていない」と誤診しました。そこで、この判定は8,000 バイト未満のときだけ当てる形にしました。大きなページは、noscript があっても本物とみなします。

  この関門は、安い設定、super、render と super の併用の、3 つの段階すべてで同じものを通します。

## 通った設定を、覚えるようにした

  昇格のしくみ自体にも、無駄がありました。それまでは、取得の関数が自分の中で設定を上げたときだけ、通った設定を覚えていました。呼び出し側が設定を切り替えて上げた分は、覚えていなかったのです。その結果、毎回 1 + 10 + 25 の36 クレジットを払っていました。覚えるようにしたので、次回からは 25 クレジットで始まります。安い設定も、定期的に試し直します。

  書き込みは、設定が変わったときだけにしています。毎回書くと、今度はデータベースの書き込み費用が増えるからです。

## 同じ日に見つかった、もう一つの汚れ

  同じ点検で、別の問題も見つかりました。商品名の「別表記」の食い違いを判定する処理が、照合の入口と、お掃除の側で、別々に書かれていました。お掃除の側にだけ、「商品の形が食い違う」という判定が無かったのです。その結果、トライアルデッキの別表記が、ブースターパックの商品に残ったままになり、毎日、誤った照合を呼び続けていました。判定を共通の関数にして、27 商品の 46 件を除去しました。

  あわせて、「更新する文書が無い」というエラー（7 日間で 64 件）も解消しました。商品は、キャッシュが古いときに読み直す。記事は、消えていたら見送って、残りの重複処理を続ける、という形にしています。

## 気づいた経緯と、ほかの「0 件」4 つの確認

  気づいたきっかけは、作業の最初に必ず見る定型の点検です。健全性のチェックに、「取得 0 件が 48 時間続いている」という警告が並びます。それまでは、そこに並んだ名前を「掲載が無いだけ」と流していました。この回は、流さずに中身を確かめました。

  同じ警告に並んでいたほかの 4 つも、1 つずつ見ました。全部、まっとうな 0 件でした。なぜ 0 件でよいのかを書き残しておかないと、次に見た人が同じ確認をやり直すことになります。

    情報源 | 確かめた中身

      三洋堂 | ページに「現在受付中の抽選販売はありません」と書いてある。取得も成功している（56KB）。

      コナミスタイル | 16 件取れるが、バッジが全部「遊戯王カードゲームポイント交換専用」。現金では買えない景品なので、設計どおり除外している。

      楽天市場 | もともと 1〜2 件が普通の情報源。14 日間で 0 件が 1,860 回、1 件が 2,345 回あった。

      ミニストップオンライン | どの設定でも取れない（20 秒の待ちを 2 回ずつ、3 設定すべてで応答なし）。抽選受付のときだけページが出る作りなので、休止の仕組みに任せている。

  同じ「0 件」でも、掲載が無い 0 件、設計どおり除く 0 件、取れていない 0 件は別のものです。件数だけでは区別できないので、中身を見て、理由を書き残しています。

## 分かったこと

> 「成功」と「中身が取れた」は、別のもの。

- 成功の判定は、中身で見る。通信が通ったことと、欲しいものが取れたことは、別です。「取れたものが 0 件」が続いたら、それ自体を異常として見張る必要があります。

- 判定は、1 か所に。同じ判定が 2 か所にあると、片方だけが古くなります。照合の入口とお掃除のように。

- エラーが出ないことは、うまくいっていることの証拠にならない。

  この記事は、2026 年 9 月 7 日のコミットと、その日の作業の記録から書きました。数字（2,410 バイト、278,322 バイト、323 回、36 と 25 クレジット）は、その記録に残っていた実測値です。

  この話に出てくるもの

- 「しょうがない」を測り直したら、取得の費用は8分の1だった (https://koshinstudio.com/notes/scrape-cost/) — 同じ取得の仕組みの、費用の記録

- 確認できなかったときに、通していた (https://koshinstudio.com/notes/fail-open/) — 同じアプリの、別の記録
