ショップディズニーの、ネット通販サイトが1周年記念でキャンペーンをしていました。
その中で、「ページのどこかにクーポンが隠れている」というのがありました。
ショップディズニーのサイト内のどこかに、10%OFF、15%OFF、30%OFFの各クーポンが隠されています。
キャスト一同、一生懸命隠しましたので、ぜひチャレンジしてみてください!
30%OFFクーポンを見つけた方は、ショップディズニーマスターです!
しかし、6/30の夜にクーポンを探したのですが、30%オフのクーポンが全然見つからりません。
twitterでも調べてみたのですが、マジで誰も見つけてなかったので、プログラム書いて探してみました(画像)。
作戦1: 画像のURLを推測する
10%と15%のクーポンはすぐ見つかる場所になったので、その画像のURLから、30%のクーポンのURLを推測します。
例えば、15%のクーポンはこのようなURLになっていました。
https://cdns7.shopdisney.disney.co.jp/is/image/ShopDisneyJP/treasure-hunt_210701_3200_400_15B_02?$xlargeFull$&fit=constrain&cropN=0,0,1,1
?
以下の部分はクエリパラメータなので無視できます。
重要なのは treasure-hunt_210701_3200_400_15B_02
この部分です。
10%や15%など、他の画像も見ていくと
treasure-hunt_210701_<幅>_<高さ>_<クーポンの割引率><アルファベット>_<数字>
となってることがわかりました。アルファベットの部分は、下記クニンした所 A, B, C がありました。また、最後の数字は、ついてない場合もあった気がします。
一応軽くコードを書いて検証はしたのですが、 URL から、 30%オフのクーポンを推測することは厳しそうでした。
軽く書いたコードはこちら
for ($w = 100; $w <= 2000; $w += 10) { for ($h = 100; $h <= 2000; $h += 10) { foreach (['', 'A', 'B'] as $suffix) { echo "({$w}, {$h})\n"; $base = 'https://cdns7.shopdisney.disney.co.jp'; $path = "/is/image/ShopDisneyJP/treasure-hunt_210701_{$w}_{$h}_30{$suffix}"; echo $path . "\n"; $uri = $base . $path; try { $this->client->get($uri); echo "アクセスできました\n"; return 0; } catch (\Exception $e) { echo $e->getMessage() . "\n"; } sleep(0.1); } } }
これで探しましたが見つかりませんでした。
作戦2: ページをクロールする
ということで、ページをクロールすることにしました。
先程の画像解析から、画像に treasure-hunt
という文字が入っているっぽいので、ページをランダムに移動しながら treasure-hunt を含む画像を探すことにします。
Laravel の Artisan Command で実装します。
ページの a タグを探し、ショップディズニー無いのページのURLを収集します。訪れたことある URL を管理しつつ、見つけた URL にランダムでアクセスしていきます。
訪れたページの img タグをみて、 treasure-hunt の画像を探していきます。
アクセス過多にならないように、アクセスごとに sleep を挟んで実行します
<?php namespace App\Console\Commands; use GuzzleHttp\Client; use Illuminate\Console\Command; use PHPHtmlParser\Dom; class ShopCruise extends Command { /** * @var Client */ private $client; /** * @var Dom */ private $dom; private $visited = []; private $candidate = []; public function __construct( ShopDisney $shop, Client $client, Dom $dom ) { parent::__construct(); $this->shop = $shop; $this->client = $client; $this->dom = $dom; } public function handle() { ini_set('memory_limit', '8G'); $this->cruise(''); $this->error('finish'); } private function cruise($path) { while(true) { $this->visited[] = $path; $uri = 'https://shopdisney.disney.co.jp' . $path; try { $response = $this->client->get($uri); $body = $response->getBody()->getContents(); $this->dom->loadStr($body); // クーポンの画像があるかもしれないのでまずはそれを探す $imgDoms = $this->dom->find('img'); foreach ($imgDoms as $imgDom) { $src = $imgDom->src; if (str_contains($src, 'treasure-hunt')) { $this->error($src); } else { // $this->info($src); } } // 見つからない場合はリンクをたどる sleep(0.1); $aDoms = $this->dom->find('a'); foreach ($aDoms as $aDom) { $next = $aDom->href; if (substr($next, 0, 1) === '/') { if (in_array($next, $this->visited)) { continue; } if (in_array($next, $this->candidate)) { continue; } $this->candidate[] = $next; } } } catch (\Throwable $e) { // なにもしない } $r = random_int(0, count($this->candidate) - 1); echo count($this->candidate) . ', ' . $r . "\t"; $path = $this->candidate[$r]; unset($this->candidate[$r]); $this->candidate = array_values($this->candidate); $this->info($path); } } }
結果
見つけることができました。
shopdisney のページを解析しまくって30%のクーポンみつけた、、、これ絶対気づかない気がする。気合入れて全ページ調べないと気づかないくらいのレベルだけど。
— うたかた/ヨシキ (@yoshiki_utakata) 2021年6月30日