獺祭の正規販売店の一覧マップを作る

獺祭を作っている旭酒造が「頼むから(正規販売店で)正規の値段で買ってくれ」という広告を出したらしい。その広告を見て思った。

「よし。正規販売店で買ってやろうじゃないか…ただし、正規販売店のリストをGoogleマップでくれ!広告にある店名のリストから、最寄り店を目grepで探せるわけがないんじゃ!」

というわけで、旭酒造のWebページから正規販売店の一覧マップを作れるようにしました。

やり方

CSVをダウンロードした後の手続きはGoogleマイマップの公式ドキュメントを参照した方が親切です。

  1. ここでCSVファイルを入手します。
  2. Googleマイマップのページに行き、新しいマップを作ります。
  3. インポートメニューから1で入手したCSVファイルをインポートします。どの列が住所でどの列がタイトルかを聞かれるので、適切に選んでください
  4. 完成

f:id:blue_jam:20171212202342p:plain

やったこと

前述のとおり、Googleマイマップはタイトルと住所が含まれたCSVファイルからマップを作れるらしい。というわけで、店名と住所が含まれたCSVを生成することが今回の目標です。 言語はJavascriptに決定。理由はJSだとHTMLのデータの抽出が楽そうだから。最近は頻繁に触っているし。(スクレイピングツールを使えばいいじゃんというツッコミは無しの方向で)

とりあえず、Webページの形式を確認しに行く。

f:id:blue_jam:20171212203128p:plain

嫌な予感がするぞ…

f:id:blue_jam:20171212203135p:plain

はい。というか、まともなフォーマットだったらExcelで完結するんですけどね。(いや、このままでもExcelでいける気がする。) 一つしか要素がないTRタグを取り除いて、二ついっぺんに処理するといい感じになりそう。

const getPage = (region)=>{
    return new Promise((resolve,reject)=>{
        const request = new XMLHttpRequest();
        request.open('GET', `http://www.asahishuzo.ne.jp/store/ja/${region}`);
        request.responseType = 'document';
        request.addEventListener('load', (e)=>{
            if (request.status === 200) {
                const doc = request.responseXML;
                const trs = doc.querySelectorAll('tbody > tr');
                const filteredTrs = [];
                for (let i = 0; i < trs.length; i++) {
                    if (trs[i].childNodes.length > 1) {
                        filteredTrs.push(trs[i]);
                    }
                }
                const map = new Map();
                for (let i = 0; i < filteredTrs.length; i += 2) {
                    map.set(filteredTrs[i].childNodes[1].innerText, filteredTrs[i + 1].childNodes[1].innerText);
                }
                resolve(map);
            }
        });
        request.addEventListener('error', (e)=>reject());
        request.send();
    })
};

const regions = ['hokkaidou', 'touhoku', 'kantou', 'cyubu', 'kansai', 'cyugoku', 'shikoku', 'kyushu', 'okinawa'];

Promise
    .all(regions.map(getPage))
    .then((maps) => {
        let resultMap = new Map();
        for (let i = 0; i < maps.length; i++) {
            resultMap = new Map([...resultMap, ...maps[i]]);
        }
        document.open();
        resultMap.forEach((name, address) => {
            document.write(`${name},${address}<br/>`);
        });
        document.close();
    });