競合サイトと突き合わせて、取りこぼしを数えた:最初の「カバー率 23%」は測り方の間違いで、本当は 49% だった

English version

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

iOS アプリ「トレカダッシュ」の目標の 1 つは、抽選販売の情報をもれなく出すことです。もれがあるかどうかを確かめるには、同じ情報を出している他のサイトと突き合わせて、「あちらにあって、こちらに無いもの」を数えるのが一番です。

2026 年 8 月 29 日の未明に、これをしました。最初に出た数字は「カバー率 23%」。ところが、それは測り方の間違いでした。この記事は、数字が 23% から 49%、そして 90% になるまでに見つけた、5 つの問題の記録です。

数字は、2026 年 8 月 29 日の作業の記録から書いています。他のサイトの名前は、出しません。

まず、全部の競合を調べた

「競合が出しているのに、このアプリが出していない記事を、無くしてほしい」という依頼でした。最初に、競合になりうるサイトを全部調べました。取り込めるかどうかは、robots.txt(機械による取得のお願い)と、利用規約を全部読んで決めました。

見つかったもの数扱い
すでに取り込んでいたサイト3そのまま
今回、取り込みを追加したサイト(A サイト)1案件の単位で、応募リンク付き
規約に禁止の条項がなく、取り込めるサイト2今回は、手作業で突き合わせ
解説記事型で、店ごとの受付一覧を持たないサイト複数差分がほぼ無いので対象外
robots.txt と規約で、自動取得を禁止しているサイト2取り込まない。応募先のリンクにも出さない
ドメインが消滅していたサイト1一覧から外す

1 つのサイトは、robots.txt の冒頭に、日本語で禁止が書かれていました。機械的に取れるからといって、取って良いわけではありません。

突き合わせの相手は、こちらより細かかった

今回追加した相手(以下、A サイト)は、「店 × 商品 × 締切 × 応募リンク」を、1 つのページに 90 件並べていました。こちらの取り込み元は記事の単位でしたが、A サイトは案件の単位で、しかも応募のリンクが、外部の応募フォームそのもの(Google フォーム、LivePocket、抽選ロトボックス、店の公式 X)でした。数えるにも、埋めるにも、条件の良い相手でした。

問題 1:商品名の書き方が違うだけで、「取りこぼし」と数えていた

最初の数字はカバー率 23% でした。理由の 1 つ目は、商品名の書き方です。

相手   30th CELEBRATION BOX
こちら 拡張パック「30th CELEBRATION」

どちらも、もう一方を含んでいないので、素朴な「含んでいるか」では、永久に一致しません。60 件が、これで「取りこぼし」と数えられていました。

ここで、「含んでいれば同じ」にしては、いけません。「30th CELEBRATION」という名前は、「30th CELEBRATION プレミアムデッキセット」にも含まれます。別の商品を出しているのに「出せている」と数える方向の間違いです。穴が見えなくなる方向に間違えます。

直し方は、弾の呼び方(拡張パック、ハイクラスパック)と、数量の言い方(BOX、n 個セット、n 種)を落としたうえで、商品カタログの ID に結び付けることでした。決める順番は、「完全一致」、「相手の名前に含まれるカタログ名のうち、最も長いもの」、「逆向きで、1 件だけのとき」です。

問題 2:期限切れの記事を、「カバー済み」と数えていた

理由の 2 つ目は、5 件でした。ある店の、7 月 29 日が締切の記事を、相手の 8 月 30 日が締切の案件と対にして、「記事はあるのに非表示」と報告していました。前の回の別の抽選です。この 2 つの直しで、正しい数字は49% でした。

問題 3:穴埋めで作った記事が、別の商品になっていた

見つかった穴は、記事を作って埋めます。ところが、その記事を作るときに、相手の商品名を、そのまま AI に渡していました。AI は、「30th CELEBRATION BOX」を、定価 27,500 円の「30th CELEBRATION FUTURISTIC BOX」に結び付けました。10 件が、別の商品の記事になりました。

数える側は、すでにカタログの商品を 1 つに絞れていました。作る側にも、こちらのカタログの名前を渡すのが正しい直し方でした。「数える判断」と「作る判断」を、1 本にします。

この修復のときに、私自身がもう 1 つ間違えました。商品の ID を、先頭 10 文字に切り詰めた値で書いてしまい、存在しない ID の記事を 10 件作りました。点検の出力を、見やすいように slice(0, 10) で切っていて、その短い値を、そのまま処理に使ったのです。すぐ直しましたが、表示のために短くした ID を、処理に使わない、が決まりごとになりました。

問題 4:応募リンクが、t.co 以外だと、必ず捨てられていた

まとめ由来の記事に、応募のリンクを付ける処理は、店ごとの URL を、リンクを展開する関数に通していました。この関数は、t.co(X の短縮 URL)しか見ません。X の投稿のリンクは、必ず短縮されるからです。X 以外の取り込み元は、応募の URL を、短縮されていない普通の形で持っています。それを通すと、必ず 0 件になり、ウェブ検索に落ちていました。

A サイトから作った 32 件のうち、17 件が、応募先ではなく、取り込み元のサイトのページを指していました。取り込み元のサイトへ利用者を飛ばすのは、禁止にしていた状態です。

直し方は、短縮されていない普通の URL は、そのまま候補にする(採用するかの検査は、従来の検査を通す)ことでした。すでにできていた記事は、作り直せないので、突き合わせのたびに応募リンクを張り替える処理を入れ、16 件を張り替えました。

問題 5:「締切が無い」で隠した記事に、戻す道がなかった

締切が分からない、まとめ由来の記事を隠す処理がありました。この処理は隠すだけで、締切があとから分かっても、永久に隠れたままでした。直っても鳴り続ける警告と、同じ形の失敗です。相手が締切を書いている案件は、締切を入れて、表示に戻すようにしました(別の理由でも隠れている記事は、戻しません)。2 件を戻しました。

このとき、締切の時刻の扱いも直しました。読み取った日付を、「日本時間ずらし」の値のまま返していて、そのまま保存すると、9 時間ずれた締切になるところでした。日本時間の 23:59 は、協定世界時の 14:59 です。本当の時刻を返すようにしました。

結果

件数
A サイトが出している、受付中の案件86
こちらで、表示中77(カバー率 90%)
フィードに無い0
記事はあるのに、非表示2
相手が商品を特定していないので、作れない7

この作業で、記事を 32 件作り、応募リンクを 16 件張り替え、締切を入れて戻したものが 2 件ありました。

残った 9 件は、全部、理由が分かっています。

分かったこと

数える道具の間違いは、数字を悪く見せるのではなく、穴を見えなくする。

この話に出てくるもの

ほかの記録

コメント

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

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