背景
そろそろSplatoon2も発売されるのでNintendo Switch(以下スイッチ)が欲しくなってきましたが、以下の課題にぶち当たりました。
- どこにも売ってない
- オンラインショップでたまに販売されるが、監視が難しい。
- オンラインショップを監視するtwitterボットもあるが、日中そのボットを監視する事自体が難しい
素直なスクレイピング
以上の課題を解決してスイッチを手に入れるため、オンラインショップをスクレイピングし、在庫を確認できたらショップ画面を開いてくれるスクリプトを書いてみました。
const CronJob = require('cron').CronJob;
const client = require('cheerio-httpcli');
const opn = require('opn');
function checkYodobashi() {
const url = 'http://www.yodobashi.com/product/100000001003431565/';
client.fetch(url, function(err, $, res, body) {
const status = $('#js_buyBoxMain p').text();
if (status !== '予定数の販売を終了しました') {
console.log('Now is the Time!!!');
opn(url);
} else {
console.log('Now is Not the Time...');
}
});
};
new CronJob('0 */15 * * * *', function() {
new Promise(checkYodobashi);
}).start();
これを各ショップごとに対応していけば、絶対にスイッチが買える…!と思っていましたが、ここで大きな問題が出てきます。
本家本元マイニンテンドーストアが毎回誤動作するのです。なぜだろうと思い、HTMLのソースを見てみると…
在庫状況がJavaScriptで更新されてる!!
前述の手法では、このページの在庫状況を正しく判断する事はできません。どこから在庫状況を取得してきているのか調べるのが面倒なので1なんとかしたい!
Headless Chromeの導入
ここで思い出したのが、Chrome 59から導入されているHeadless Chromeの存在です。
Headless Chromeをそのまま扱うのは結構面倒なので、chromyというライブラリを利用します。
const CronJob = require('cron').CronJob;
const Chromy = require('chromy');
const opn = require('opn');
const shops = {
nintendo: {
url: 'https://store.nintendo.co.jp/customize.html',
name: 'My Nintendo Store',
checkStore: client => {
return client.evaluate(() => {
return document.querySelector('#HAC_S_KAYAA p.stock').innerText !== 'SOLD OUT';
});
}
},
};
async function check(client, options) {
try {
await client.goto(options.url);
const result = await options.checkStore(client);
if (result) {
console.log(`Now is the Time!!!`);
opn(options.url);
} else {
console.log(`Now is Not the Time...`);
}
} catch(e) {
console.log(e);
}
}
async function main () {
let chromy = new Chromy();
await check(chromy, shops.nintendo);
await chromy.close();
}
new CronJob('0 */15 * * * *', main).start();
在庫状況を正しく取得する事が出来ました!(在庫があった場合の確認はできていません😇)
そしてこれをベースに、様々なショップ情報を追加したスクリプトがこちらとなります。
これでスイッチを手に入れよう!(転売目的でのご利用はやめてください!)
なお著者はこのスクリプトを作成後、実店舗の抽選販売でスイッチを手に入れる事ができました。
-
この記事を書くために処理をちゃんと追ってみると、在庫状況用を取得するAPIなどは叩いておらず、在庫状況を
display: none;
の要素から引っ張ってきている事が分かった。なぜこんな実装に…。 ↩