LoginSignup
1
4

More than 3 years have passed since last update.

【Javascript】カートの中身を自動的にCSV出力するスクリプトで会計処理を楽にする

Posted at

作るに至った経緯

会計処理の際に購入予定のもの情報を一つ一つExcelにコピーして貼り付ける作業が毎回手作業なので、どうにか時間短縮できないかなと思い、自動化するスクリプトを作ることにしました。NodeやSeleniumだと既存のブラウザにアタッチできないのでjavascriptを使いました。

今回やったこと

モノタロウの通販サイトにログインし、カートに入っている全ての商品の情報をCSVファイルに自動でまとめてくれるスクリプトを作りました。
ちなみに秋月電子は
https://tlt.gurigoro.net/entry/2018/01/13/200510 この拡張機能を使ってまとめるとよいです。
できるだけ有りものは利用しましょう。

環境

Windows10 Home
CPU: Intel Core i5 7200U
tampermonkey
javascript
Google Chrome

本題

前提

モノタロウの通販サイトで既に買いたい商品がカートに入っていて、ログインが済んでいる。カートのページにいる。

プログラムの流れ

モノタロウの商品カートから情報を取得
取得した情報の配列を作る
配列の整形
配列からCSVに変換する
CSVファイルをダウンロードする

ブラウザでCSVをダウンロードする方法

Google chrome上のjavascriptでCSVファイルをダウンロードする方法を学びました。まずはこちらのサイトを参考に情報を取得した前提でCSVをダウンロードしてみます。ちなみにこのCSVファイルはwindowsでないと文字化けします。詳しくはjavascript で作成したCSVファイルをエクセルで表示可能にするを参考にしてください。

tampermonkeyの導入

まずはchrome拡張 tampermonkeyを入れます。登録したページに行くとjavascriptを実行してくれる拡張機能です。
詳細はこちらの記事が参考になります。

カートの中身の情報をスクレイピングする

tampermonkeyという拡張機能を使って製品名などの情報を取得し、csvに出力します。残念ながら今の私の技能ではtampermonkeyとspredsheetを連携させることは無理でした。

要素の取得はCSSセレクタを手打ちで入力していきました。コーディングのポイントは、そのタグやクラスの名前が固有のものだったらそのまま書く、固有でなかったら子要素を利用して絞り込みます。item_contentなど、親要素の名前が同じ場合がありますが、子要素のクラス名が独特なものなので直接指定しています。入れ子にするかは状況をみて使い分けましょう。

tampermonkeyでは(function() {})(){}の中にコードを書いて実行します。
@matchは実行するページのURLを書きます。ここに書いたページに遷移すると実行されるので、カートに入るボタンを押したら実行されることになります。

MonotaroCart_to_csv.js
// ==UserScript==
// @name         カートの中身CSV出力くん
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://www.monotaro.com/monotaroMain.py?func=monotaro.basket.showListServlet.ShowListServlet&RtnPage=%2Fg%2F04470570%2F%3Fes%3D1
// @grant        none
// ==/UserScript==

(function() {
let product_name = [];
let oneset_num = [];
let price = [];
let buy_num = [];
let url = [];
let subtotal = [];
const Store = 'モノタロウ';
let heder_label = ['用途', '種別', '製品名', '購入場所', '内容量', '単価', '購入数量', 'URL', '小計', '合計'];

const ProductInfo = () => {
    let elm = document.querySelectorAll('.product_td');//商品ブロックの要素を取得
    let price_elm = document.querySelectorAll('td.price_td');//金額ブロックの要素を取得
    for (let i = 0; i < elm.length; i++) {
        product_name[i] = elm[i].querySelector('h4>a').innerText;//製品名を取得
        //oneset_num[i] = elm[i].querySelector('li>#text').innerText;//内容量を取得
        url[i] = elm[i].querySelector('h4>a').href;//urlを取得
    }
    for (let i = 0; i < price_elm.length; i++) {
        price[i] = price_elm[i].querySelector('td.item_content').innerText;//値段
        buy_num[i] = price_elm[i].querySelector('input.button_items_quantity.imemode_inactive').value;//購入数量
        subtotal[i] = price_elm[i].querySelector('em').innerText;//小計

    }
}



class CSV {
    constructor(data, keys = false) {
        this.ARRAY = Symbol('ARRAY');
        this.OBJECT = Symbol('OBJECT');

        this.data = data;

        if (CSV.isArray(data)) {
            if (0 == data.length) {
                this.dataType = this.ARRAY
            } else if (CSV.isObject(data[0])) {
                this.dataType = this.OBJECT
            } else if (CSV.isArray(data[0])) {
                this.dataType = this.ARRAY
            } else {
                throw Error('Error: 未対応のデータ型です')
            }
        } else {
            throw Error('Error: 未対応のデータ型です')
        }

        this.keys = keys
    }

    toString() {
        if (this.dataType === this.ARRAY) {
            return this.data.map((record) => (
                record.map((field) => (
                    CSV.prepare(field)
                )).join(',')
            )).join('\n')
        } else if (this.dataType === this.OBJECT) {
            const keys = this.keys || Array.from(this.extractKeys(this.data))

            const arrayData = this.data.map((record) => (
                keys.map((key) => record[key])
            ))

            console.log([].concat([keys], arrayData))

            return [].concat([keys], arrayData).map((record) => (
                record.map((field) => (
                    CSV.prepare(field)
                )).join(',')
            )).join('\n')
        }
    }

    save(filename = 'data.csv') {
        if (!filename.match(/\.csv$/i)) { filename = filename + '.csv' }

        console.info('filename:', filename);
        console.table(this.data);

        const csvStr = this.toString();

        const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
        const blob = new Blob([bom, csvStr], { 'type': 'text/csv' });
        const url = window.URL || window.webkitURL;
        const blobURL = url.createObjectURL(blob);

        let a = document.createElement('a');
        a.download = decodeURI(filename);
        a.href = blobURL;
        a.type = 'text/csv';

        a.click();
    }

    extractKeys(data) {
        return new Set([].concat(...this.data.map((record) => Object.keys(record))));
    }

    static prepare(field) {
        return '"' + ('' + field).replace(/"/g, '""') + '"';
    }

    static isObject(obj) {
        return '[object Object]' === Object.prototype.toString.call(obj);
    }

    static isArray(obj) {
        return '[object Array]' === Object.prototype.toString.call(obj);
    }
}


const MakeList = () => {
    let csvfile = [heder_label,];
    for (let i = 0; i < product_name.length; i++) {
        csvfile.push([, , product_name[i], Store, oneset_num[i], price[i], buy_num[i], url[i], subtotal[i]]);
    }

    return csvfile;
}
//console.log(MakeList());
ProductInfo();
(new CSV(MakeList())).save('buylist.csv');//csvオブジェクトの作成 引数にはファイル名を指定
})();

新しいスクリプトをtampermonkeyに書いて有効にしたらモノタロウのカートに移動します。すると、以下のようなテーブルでcsvファイルが作成され、CSVファイルがダウンロードできます。
buylist.PNG

最後に

seleniumだと環境構築が面倒ですが、tampermonkeyは拡張機能のインストールだけで動くので非常に簡単でいいですね。
今回でjavascriptを使ったスクレイピングはできるようになったので、今後は別のECサイトの分もつくりたいです。

参考
https://qiita.com/sagami1991/items/794168cf14ed198d6372
https://blog.mudatobunka.org/entry/2017/04/23/135753

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4