Web、サーバ、ソフトウェア、バグ・脆弱性 などの情報を何人かで集まって書いていく IT/Web情報系ブログ

Switchの在庫チェックコード

投稿日:   最終更新日:2017/08/04  投稿者:ktm@s

任天堂スイッチをフレンズ用に探していたのだが、つい先ほど購入が確定したので在庫チェックコードをかく。
脳汁がでまくってお祭り気分なので本来晒すようなコードじゃないんだけど・・・。

スイッチ難民の争い

3月に任天堂スイッチを手にしていた身からすると、スプラ2を前に5万円を超えた任天堂スイッチ難民の戦争は、正直別世界のようで実感がなかった。
しかし周りにスプラトゥーン2やりたいけどスイッチ無い派がそこそこいたため、炎天下の中抽選に行ったりオンラインに張り付いたりしたけど正直地獄でしかなかった。

2Fにある店舗の窓に張ってあるPOPだけの告知で10台抽選した店は、期限内のたった1時間に140人くらい来たし、いつ在庫が来るかわからないオンラインショップでは情報を得たころにはもはやサイトは軽く、当然スイッチが買えるはずもないのであった。

Amazonの在庫をF5連打でチェックしていると、転売品の5万円近くする在庫がじわじわ減っていく・・・。正直ナメてた。
だからいつも使っているMattermostというチャットプログラムに在庫通知BOTを作った!

スポンサーリンク

チェックコード

マイニンテンドーストア

結果的にマイニンテンドーストアで購入できた。
(恐らく)18:00に販売開始という任天堂の予定で、実際にカートに追加できたのは1,2分後だったと思う。
前回は40分くらい持ったという情報があったので楽観視していたのだが、完売したのは5,6分後だと思う。競争率の高さが伺えた。

イカのコードは今回実際にマイニンテンドーストアの在庫を判定したコードそのままであり、このコードは実際にアクセスした際にまだカートが有効でなかった時間から在庫を告知してくれた。
(htmlのSOULD OUT部分を見ているのでスタッフが一時的に手を加えたと思われる)

# マイニンテンドーストア
$url  = "https://store.nintendo.co.jp/customize.html";
$html   = file_get_contents($url);
if($html !== false){
	preg_match('/<div\sclass="customize_price__priceInner">(.+?)<\/div>/s', $html, $gomi);
	preg_match('/<p\sclass="stock">(.+?)<\/p>/s', $gomi[0], $gomi2);
	if($gomi2[1] !== "SOLD OUT"){
		在庫があるだろう時に行いたい処理
	}
}

上記コードを1分置きにcronでまわしていた。

MattermostBOT(実際に運用してた)コード

実際に運用してたコードはイカのとおりになる。
http_post関数で必要な項目を自分が使っているMattermostのものに書き換えるとそのまま動くと思う。
なお、マイニンテドーストアとノジマオンラインは100%の在庫的中。
あみあみに関しては別の転売ヤーが殺到する何か(確か仮面ライダーのうんたら)で一時誤判定した。

<?php
# マイニンテンドーストア
$s_name1 = "マイニンテンドーストア";
$s_url1  = "https://store.nintendo.co.jp/customize.html";
$html1   = file_get_contents($s_url1);
if($html1 !== false){
	preg_match('/<div\sclass="customize_price__priceInner">(.+?)<\/div>/s', $html1, $gomi);
	preg_match('/<p\sclass="stock">(.+?)<\/p>/s', $gomi[0], $gomi2);
	if($gomi2[1] !== "SOLD OUT"){
		http_post($s_name1, $s_url1);
	}
}

# 7net
$s_name2 = "セブンネット";
$s_url2  = array(
	"https://7net.omni7.jp/detail/2110599526",
	"https://7net.omni7.jp/detail/2110595637",
	"https://7net.omni7.jp/detail/2110595636",
	"https://7net.omni7.jp/detail/2110596901",
	"https://7net.omni7.jp/detail/2110596900"
);
foreach($s_url2 as $val){
	$html2 = file_get_contents($val);
	if($html2 !== false){
		preg_match('/linkBtn\sjs-pressTwice"\stype="submit"\svalue="(.+?)"/', $html2, $sevennet);
		if($sevennet[1] !== "販売終了" and $sevennet[1] !== "在庫切れ"){
			http_post($s_name2, $val);
		}
	}
}

/*
# 楽天ブックス
$s_name3 = "楽天ブックス";
$s_url3  = array(
	"http://ts.books.rakuten.co.jp/rb/14647221/",
	"http://ts.books.rakuten.co.jp/rb/14647222/",
	"http://ts.books.rakuten.co.jp/rb/14943334/"
);
foreach($s_url3 as $val2){
	$html3 = file_get_contents($val2);
	if($html3 !== false){
		preg_match('/<span\sclass="status">(.+?)<\/span>/s', $html3, $rbooks);
		#EUCのため以下がマッチしない
		if($rbooks[1] !== "ご注文できない商品*"){
			http_post($s_name3, $val2);
		}
	}
}
*/

# ノジマオンライン
$s_name4 = "ノジマオンライン";
$s_url4  = "https://online.nojima.co.jp/category/10006903/?searchCategoryCode=10006903&mode=image&pageSize=15&currentPage=1&alignmentSequence=1&searchDispFlg=true&attributeValue=0_5&immediateDeliveryDispFlg=1";
$html4   = file_get_contents($s_url4);
if($html4 !== false){
	if(preg_match('/お探しの商品が見つかりませんでした/', $html4) == 0){
		http_post($s_name4, $s_url4);
	}
}

# あみあみ
$s_name5 = "あみあみ";
$s_url5  = array(
	"http://www.amiami.jp/top/detail/detail?gcode=GAME-0017598&page=top%2Fsearch%2Flist%3Fs_cate4%3D10061%24pagemax%3D40%24getcnt%3D0%24pagecnt%3D1",
	"http://www.amiami.jp/top/detail/detail?gcode=GAME-0017599&page=top%2Fsearch%2Flist%3Fs_cate4%3D10061%24pagemax%3D40%24getcnt%3D0%24pagecnt%3D1",
	"http://www.amiami.jp/top/detail/detail?gcode=GAME-0018286&page=top%2Fsearch%2Flist%3Fs_cate4%3D10061%24pagemax%3D40%24getcnt%3D0%24pagecnt%3D1"
);
foreach($s_url5 as $val3){
	$html5 = file_get_contents($val3);
	if($html5 !== false){
		if(preg_match('/販売停止中/', $html5) == 0){
			http_post($s_name5, $val3);
		}
	}
}

# Amazon 金額で絞れないので放置
$s_name6 = "Amazon";
$s_url6  = "https://www.amazon.co.jp/s/ref=sr_nr_p_n_condition-type_0?fst=as%3Aoff&rh=n%3A637394%2Cn%3A4731377051%2Cn%3A4731379051%2Ck%3A%E3%80%90Amazon.co.jp%E9%99%90%E5%AE%9A%E3%80%91Nintendo+Switch%2Cp_n_availability%3A2227306051%2Cp_n_price_fma%3A401108011%2Cp_n_condition-type%3A680602011&sort=price-asc-rank&keywords=%E3%80%90Amazon.co.jp%E9%99%90%E5%AE%9A%E3%80%91Nintendo+Switch&ie=UTF8&qid=1501399071&rnid=680601011";


# 通知関数
function http_post($shop, $shopurl){
	$url  = "Mattermostの内向きのウェブフックURL";
	$name = "Switchの在庫来たかも";
	$msg  = "ショップ:" . $shop . "\nURL:" . $shopurl;
	$data = json_encode(array("icon_url" => "アイコンURL", "username" => $name, "text" => $msg));
	return array (
		'content'=>  file_get_contents (
			$url,
			false,
			stream_context_create (
				array ('http' =>
					array (
						'method'=>'POST',
						'header'=>"Content-Type: application/json; charset=utf-8",
						'content'=>$data)
				)
			)
		)
	);
}
?>

cronに登録する際の注意点

cronは1分置きになるようイカのように登録した。
しかし1>/dev/nullではデバッグ時に役に立ったものの503エラーなどで大量のメールが自身に届いたので、エラー出力も破棄するのがベターだろう。

*/1 * * * * php ファイル絶対パス 1>/dev/null

あとがき

買えて良かった!!!!

正直スイッチが無い無い言ってる人たちの事は、予約ないし現実的な倍率の抽選が発売日からしばらくあったのに今更何を言ってるんだ?と思っていたが、ここまで熾烈な争いとは思っておらず、実際に参戦してみて自分勝手な偏見だったことが露呈した。

自分はプログラミングもできないのでこのレベルの酷いコードを晒すのに抵抗が無くもないのだが、大体一瞬で在庫が尽きる楽天ブックスやノジマオンラインと違い、マイニテンドーストアに関してはそれなりの在庫とウェブサーバーの強さが見込めるのでチャレンジして欲しい。

実際に上記コードのBOTを作って苦節3日。
このような在庫判定BOTなんてかなりお行儀が良いほうで、実際には(中華系の転売グループは特に)購入手続きまで自動化されてしまっていて、一般客がチャレンジできる可能性がかなり下がっているように主観ではあるが感じた。

そろそろ購入まで自動化するBOTを書こうと思っていた矢先に手に入ったのでラッキーであったが、この記事を読んだ皆さまもせめて在庫があるかどうかだけは自動化したほうがよいだろうと思う。

Librahackの件を忘れることなく無理の無い巡回にしてあげてね。

(余談ではあるが、Twitterの在庫をチェックして告知してくれるBOTは遅いことがわかった)

- PHP , , ,

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事