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

English version

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

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

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

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

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

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

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

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

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

取得の設定応答中身
安い設定(1 クレジット)2002,410 バイト。「このサイトでは JavaScript を有効にする必要があります」だけ
super + render(25 クレジット)200278,322 バイト。商品 39 件

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

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

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

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

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

  1. 強い設定に切り替え直さない。商品は 0 件のまま。
  2. 「安い設定で取れる」と覚えて、強い設定への切り替えを解除する。
  3. 以後ずっと 0 件。エラーが 1 件も出ないので、ログにも何も残らない。

3 日間の数字

日時ポケモンセンターの取得件数
9 月 4 日 12:45 まで10 件か 8 件(安定していた)
9 月 4 日 12:45 から 9 月 7 日0 件が 323 回連続。エラーは 1 件もなし
直した後に手で実行10 件に戻った

直し方

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

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

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 件は別のものです。件数だけでは区別できないので、中身を見て、理由を書き残しています。

分かったこと

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

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

この話に出てくるもの

ほかの記録

コメント

感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。

押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。