dayjournal memo

Total 992 articles!!

Try #001 - 歩数情報をスクレイピングして可視化してみた

Yasunori Kirimoto's avatar

try_001_09


今回は、Node.jsとC3.jsを利用してこのblogの「Walking」から歩数情報をスクレイピングして可視化してみようと思います。


地味にこんな感じで週単位で自分の歩数を記録して公開しています。 try_001_05


はじめに歩数情報を取得する方法を試していきます。



①取得したいURLを実装

取得したい情報が存在するURLを確認してみます。URLは「https://day-journal.com/blog/walking-001/」、「https://day-journal.com/blog/walking-002/」のように複数ページで規則性があるURLのようです。 try_001_07


それでは、「add_URL_all」という配列にそれぞれのURLを格納する処理を実装してみます。


    function (callback) {
            for (i = 1; i <= 43; i++) {
                //URLを指定
                add_URL = "https://day-journal.com/blog/walking-";
                //URLに連番を付与
                if (i < 10) {
                    add_URL = add_URL + "00" + i + "/";
                } else {
                    add_URL = add_URL + "0" + i + "/";
                }
                add_URL_all[i] = add_URL;
                //console.log(add_URL_all[i]);
            }
            //3秒待機
            setTimeout(callback, 3000);
            console.log('URL取得完了!!');
    },


②スクレイピング処理を実装

まず、取得したい情報がどんなhtml構造か確認してみます。 try_001_06


どうやらtdタグで情報が取得できそうですね。それではページをスクレイピングして取得した情報を各変数に格納する処理を実装してみます。スクレイピングするために今回は、HTMLをjQueryライクにパースしてくれる「cheerio-httpcli」を利用してみます。


    function (callback) {
            //フィールド名を定義
            data_moto = "日付,曜日,歩数\r\n";
            //ループ初期化
            loopIndex = 1;
            //URLの数だけループ処理
            async.whilst(function () {
                    return loopIndex < i;
                },
                function (cb) {
                    //スクレイピングするURLをコンソールに表示
                    console.log('URL: ' + add_URL_all[loopIndex]);
                    //スクレイピング処理
                    client.fetch(add_URL_all[loopIndex], para, function (err, $, res) {
                        //カウント・フラグ初期化
                        count = 1;
                        flag = 0;
                        //tdタグの総数を取得
                        td_length = $('td').length;
                        //tdタグを取得
                        $("td").each(function (idx) {
                            //1.dataの取得 曜日別に集計
                            if (count % 3 === 0) {
                                data[flag] = data[flag] + parseInt($(this).text());
                                flag = flag + 1;
                            }
                            //2.data_motoの取得 最後の合計値は除外
                            if (count !== td_length) {
                                //tdの値をdataに入れる
                                data_moto = data_moto + $(this).text();
                                //値の間に改行か,を挿入
                                if (count % 3 == 0) {
                                    data_moto = data_moto + "\r\n";
                                } else {
                                    data_moto = data_moto + ",";
                                }
                            }
                            //tdタグをカウント
                            count = count + 1;
                        });
                    });
                    //ループカウント
                    loopIndex = loopIndex + 1;
                    //1秒間隔でスクレイピング処理
                    setTimeout(cb, 1000);
                }
            );
            //次の処理まで50秒待機
            setTimeout(callback, 50000);
    },

「async」のasync.whilstを利用して取得したいURLの分だけ処理を実行:


            //URLの数だけループ処理
            async.whilst(function () {
                    return loopIndex < i;
                },

「cheerio-httpcli」を利用してtdタグを取得してdataに「曜日別」、data_motoに「日別」情報を格納:


                        $("td").each(function (idx) {
                            //1.dataの取得 曜日別に集計
                            if (count % 3 === 0) {
                                data[flag] = data[flag] + parseInt($(this).text());
                                flag = flag + 1;
                            }
                            //2.data_motoの取得 最後の合計値は除外
                            if (count !== td_length) {
                                //tdの値をdataに入れる
                                data_moto = data_moto + $(this).text();
                                //値の間に改行か,を挿入
                                if (count % 3 == 0) {
                                    data_moto = data_moto + "\r\n";
                                } else {
                                    data_moto = data_moto + ",";
                                }
                            }
                            //tdタグをカウント
                            count = count + 1;
                        });
                    });

サーバーに負荷をかけないために1秒間隔でスクレイピング処理を実行:


                    //ループカウント
                    loopIndex = loopIndex + 1;
                    //1秒間隔でスクレイピング処理
                    setTimeout(cb, 1000);
                }


③CSVに保存する処理を実装

次に、スクレイピング処理で取得した値をCSVに格納する処理を実装してみます。


    function (callback) {
            //フィールド名を定義
            data_all = "日,月,火,水,木,金,土\r\n";
            for (i = 0; i <= 6; i++) {
                //値の間に改行か,を挿入
                if (i === 6) {
                    data_all = data_all + data[i] + "\r\n";
                } else {
                    data_all = data_all + data[i] + ",";
                }
            }
            //data_all変数をCSVに吐き出し
            fs.writeFile('./Walking.csv', data_all);
            //日付の「.」を「-」に文字置換
            result = data_moto.split(".").join("-");
            //result変数をCSVに吐き出し
            fs.writeFile('./Walking_moto.csv', result);
            //3秒待機
            setTimeout(callback, 3000);
            console.log('CSV作成完了!!');
    }

スクレイピング処理で「曜日別」に取得したデータをdata_all変数に整形格納:


            //フィールド名を定義
            data_all = "日,月,火,水,木,金,土\r\n";
            for (i = 0; i <= 6; i++) {
                //値の間に改行か,を挿入
                if (i === 6) {
                    data_all = data_all + data[i] + "\r\n";
                } else {
                    data_all = data_all + data[i] + ",";
                }
            }

「曜日別と「日別」をそれぞれのCSVに保存:


            //data_all変数をCSVに吐き出し
            fs.writeFile('./Walking.csv', data_all);
            //日付の「.」を「-」に文字置換
            result = data_moto.split(".").join("-");
            //result変数をCSVに吐き出し
            fs.writeFile('./Walking_moto.csv', result);


④上記①②③を組み合わせた処理を実装

最後に、「async」を利用して処理の順序を設定して上記コードを組み合わせて実装します。

処理を実行する前にnpmから下記の必要なモジュールをインストールします。 ・node.jsの実行順序を制御できる「async」 ・HTMLをjQueryライクにパースしてくれる「cheerio-httpcli」

作業フォルダでnpmでasyncをローカルインストールします。


npm install async

try_001_03


作業フォルダでnpmでcheerio-httpcliをローカルインストールします。


npm install cheerio-httpcli

try_001_04


処理を組み合わせた最終的なscript.jsを実装します。

script.js


var client = require('cheerio-httpcli');
var fs = require('fs');
var async = require('async');

var para = {};
var data_moto;
var data = [0, 0, 0, 0, 0, 0, 0];
var data_all;
var add_URL;
var add_URL_all = [];
var i;
var count;
var loopIndex;
var flag;
var td_length;
var result;

async.series([
    //スクレイピングするURLを設定
    function (callback) {
            for (i = 1; i <= 43; i++) {
                //URLを指定
                add_URL = "https://day-journal.com/blog/walking-";
                //URLに連番を付与
                if (i < 10) {
                    add_URL = add_URL + "00" + i + "/";
                } else {
                    add_URL = add_URL + "0" + i + "/";
                }
                add_URL_all[i] = add_URL;
                //console.log(add_URL_all[i]);
            }
            //3秒待機
            setTimeout(callback, 3000);
            console.log('URL取得完了!!');
    },

    //スクレイピング
    function (callback) {
            //フィールド名を定義
            data_moto = "日付,曜日,歩数\r\n";
            //ループ初期化
            loopIndex = 1;
            //URLの数だけループ処理
            async.whilst(function () {
                    return loopIndex < i;
                },
                function (cb) {
                    //スクレイピングするURLをコンソールに表示
                    console.log('URL: ' + add_URL_all[loopIndex]);
                    //スクレイピング処理
                    client.fetch(add_URL_all[loopIndex], para, function (err, $, res) {
                        //カウント・フラグ初期化
                        count = 1;
                        flag = 0;
                        //tdタグの総数を取得
                        td_length = $('td').length;
                        //tdタグを取得
                        $("td").each(function (idx) {
                            //1.dataの取得 曜日別に集計
                            if (count % 3 === 0) {
                                data[flag] = data[flag] + parseInt($(this).text());
                                flag = flag + 1;
                            }
                            //2.data_motoの取得 最後の合計値は除外
                            if (count !== td_length) {
                                //tdの値をdataに入れる
                                data_moto = data_moto + $(this).text();
                                //値の間に改行か,を挿入
                                if (count % 3 == 0) {
                                    data_moto = data_moto + "\r\n";
                                } else {
                                    data_moto = data_moto + ",";
                                }
                            }
                            //tdタグをカウント
                            count = count + 1;
                        });
                    });
                    //ループカウント
                    loopIndex = loopIndex + 1;
                    //1秒間隔でスクレイピング処理
                    setTimeout(cb, 1000);
                }
            );
            //次の処理まで50秒待機
            setTimeout(callback, 50000);
    },

    //スクレイピング結果をCSVに保存
    function (callback) {
            //フィールド名を定義
            data_all = "日,月,火,水,木,金,土\r\n";
            for (i = 0; i <= 6; i++) {
                //値の間に改行か,を挿入
                if (i === 6) {
                    data_all = data_all + data[i] + "\r\n";
                } else {
                    data_all = data_all + data[i] + ",";
                }
            }
            //data_all変数をCSVに吐き出し
            fs.writeFile('./Walking.csv', data_all);
            //日付の「.」を「-」に文字置換
            result = data_moto.split(".").join("-");
            //result変数をCSVに吐き出し
            fs.writeFile('./Walking_moto.csv', result);
            //3秒待機
            setTimeout(callback, 3000);
            console.log('CSV作成完了!!');
    }

],

    //集約処理
    function (err, results) {
        if (err) {
            throw err;
        }
        console.log('全ての処理完了!!');
    }
);

作業フォルダでscript.jsを実行します。しばらく待つとスクレイピングされた結果がCSVファイルで保存されます。


node script.js

try_001_01

try_001_02


try_001_08


Walking.csv


日,月,火,水,木,金,土
288500,296903,292832,274209,283144,307202,336896

Walking_moto.csv


日付,曜日,歩数
2015-05-31,日,8231
2015-06-01,月,4996
2015-06-02,火,5502
2015-06-03,水,3358
2015-06-04,木,4515
2015-06-05,金,4259
2015-06-06,土,4759
2015-06-07,日,12481
2015-06-08,月,5703
2015-06-09,火,6020
2015-06-10,水,5535
2015-06-11,木,6284
2015-06-12,金,6729
2015-06-13,土,9245
2015-06-14,日,8234
2015-06-15,月,8051
2015-06-16,火,5739
2015-06-17,水,4562
2015-06-18,木,4554
2015-06-19,金,5888
2015-06-20,土,7985
2015-06-21,日,9088
2015-06-22,月,3150
2015-06-23,火,5292
2015-06-24,水,6399
2015-06-25,木,5607
2015-06-26,金,4980
2015-06-27,土,10749
2015-06-28,日,5775
2015-06-29,月,4696
2015-06-30,火,4568
2015-07-01,水,4516
2015-07-02,木,9220
2015-07-03,金,12927
2015-07-04,土,11290
2015-07-05,日,11462
2015-07-06,月,4559
2015-07-07,火,11889
2015-07-08,水,4784
2015-07-09,木,5218
2015-07-10,金,12203
2015-07-11,土,10543
2015-07-12,日,13090
2015-07-13,月,12604
2015-07-14,火,4663
2015-07-15,水,5467
2015-07-16,木,4877
2015-07-17,金,7994
2015-07-18,土,7345
2015-07-19,日,10975
2015-07-20,月,3817
2015-07-21,火,10104
2015-07-22,水,5475
2015-07-23,木,6361
2015-07-24,金,4324
2015-07-25,土,5636
2015-07-26,日,11238
2015-07-27,月,13124
2015-07-28,火,5648
2015-07-29,水,5480
2015-07-30,木,4748
2015-07-31,金,4273
2015-08-01,土,7670
2015-08-02,日,9824
2015-08-03,月,6576
2015-08-04,火,4444
2015-08-05,水,13884
2015-08-06,木,6626
2015-08-07,金,6360
2015-08-08,土,7248
2015-08-09,日,4014
2015-08-10,月,4494
2015-08-11,火,5536
2015-08-12,水,4571
2015-08-13,木,6435
2015-08-14,金,9561
2015-08-15,土,11346
2015-08-16,日,8652
2015-08-17,月,6210
2015-08-18,火,4292
2015-08-19,水,4699
2015-08-20,木,7902
2015-08-21,金,6391
2015-08-22,土,6980
2015-08-23,日,8756
2015-08-24,月,5004
2015-08-25,火,6703
2015-08-26,水,5003
2015-08-27,木,5826
2015-08-28,金,5179
2015-08-29,土,10906
2015-08-30,日,7423
2015-08-31,月,8526
2015-09-01,火,11813
2015-09-02,水,2934
2015-09-03,木,6188
2015-09-04,金,6294
2015-09-05,土,4775
2015-09-06,日,4758
2015-09-07,月,6199
2015-09-08,火,5289
2015-09-09,水,3981
2015-09-10,木,4637
2015-09-11,金,5109
2015-09-12,土,7297
2015-09-13,日,3749
2015-09-14,月,6384
2015-09-15,火,6684
2015-09-16,水,5196
2015-09-17,木,5102
2015-09-18,金,6252
2015-09-19,土,3969
2015-09-20,日,8951
2015-09-21,月,6894
2015-09-22,火,7124
2015-09-23,水,5533
2015-09-24,木,4576
2015-09-25,金,4669
2015-09-26,土,18777
2015-09-27,日,5231
2015-09-28,月,5437
2015-09-29,火,4541
2015-09-30,水,5038
2015-10-01,木,4907
2015-10-02,金,5939
2015-10-03,土,7004
2015-10-04,日,4576
2015-10-05,月,6278
2015-10-06,火,5324
2015-10-07,水,5083
2015-10-08,木,5007
2015-10-09,金,4745
2015-10-10,土,4526
2015-10-11,日,3500
2015-10-12,月,5708
2015-10-13,火,4639
2015-10-14,水,3802
2015-10-15,木,4696
2015-10-16,金,4037
2015-10-17,土,5247
2015-10-18,日,7987
2015-10-19,月,4463
2015-10-20,火,14501
2015-10-21,水,4400
2015-10-22,木,7239
2015-10-23,金,4969
2015-10-24,土,4050
2015-10-25,日,9834
2015-10-26,月,4673
2015-10-27,火,3650
2015-10-28,水,6527
2015-10-29,木,5606
2015-10-30,金,4531
2015-10-31,土,9416
2015-11-01,日,8546
2015-11-02,月,4248
2015-11-03,火,4870
2015-11-04,水,3659
2015-11-05,木,4547
2015-11-06,金,3215
2015-11-07,土,8549
2015-11-08,日,9861
2015-11-09,月,4712
2015-11-10,火,3126
2015-11-11,水,4294
2015-11-12,木,4157
2015-11-13,金,4875
2015-11-14,土,11597
2015-11-15,日,8496
2015-11-16,月,5258
2015-11-17,火,3649
2015-11-18,水,3930
2015-11-19,木,4755
2015-11-20,金,5097
2015-11-21,土,7944
2015-11-22,日,12340
2015-11-23,月,4987
2015-11-24,火,6868
2015-11-25,水,8744
2015-11-26,木,10131
2015-11-27,金,8129
2015-11-28,土,3241
2015-11-29,日,5367
2015-11-30,月,8545
2015-12-01,火,8876
2015-12-02,水,8732
2015-12-03,木,6160
2015-12-04,金,8139
2015-12-05,土,7258
2015-12-06,日,8639
2015-12-07,月,9878
2015-12-08,火,7713
2015-12-09,水,9271
2015-12-10,木,7684
2015-12-11,金,6884
2015-12-12,土,9826
2015-12-13,日,6423
2015-12-14,月,7277
2015-12-15,火,4722
2015-12-16,水,6820
2015-12-17,木,9306
2015-12-18,金,8018
2015-12-19,土,4055
2015-12-20,日,3285
2015-12-21,月,7695
2015-12-22,火,8513
2015-12-23,水,5089
2015-12-24,木,6402
2015-12-25,金,8974
2015-12-26,土,8129
2015-12-27,日,1772
2015-12-28,月,11485
2015-12-29,火,8643
2015-12-30,水,9262
2015-12-31,木,1658
2016-01-01,金,5076
2016-01-02,土,6418
2016-01-03,日,5487
2016-01-04,月,6660
2016-01-05,火,8197
2016-01-06,水,8290
2016-01-07,木,8012
2016-01-08,金,7316
2016-01-09,土,9299
2016-01-10,日,2489
2016-01-11,月,8206
2016-01-12,火,7721
2016-01-13,水,8239
2016-01-14,木,8842
2016-01-15,金,7720
2016-01-16,土,6082
2016-01-17,日,2154
2016-01-18,月,8498
2016-01-19,火,8909
2016-01-20,水,8353
2016-01-21,木,7917
2016-01-22,金,9836
2016-01-23,土,8641
2016-01-24,日,4121
2016-01-25,月,8349
2016-01-26,火,8939
2016-01-27,水,9336
2016-01-28,木,8451
2016-01-29,金,8881
2016-01-30,土,8546
2016-01-31,日,2769
2016-02-01,月,8494
2016-02-02,火,7809
2016-02-03,水,7711
2016-02-04,木,7777
2016-02-05,金,7298
2016-02-06,土,8255
2016-02-07,日,2432
2016-02-08,月,10522
2016-02-09,火,7925
2016-02-10,水,9162
2016-02-11,木,8742
2016-02-12,金,8962
2016-02-13,土,9217
2016-02-14,日,2857
2016-02-15,月,9689
2016-02-16,火,8365
2016-02-17,水,8510
2016-02-18,木,8472
2016-02-19,金,8189
2016-02-20,土,8411
2016-02-21,日,2321
2016-02-22,月,9263
2016-02-23,火,5413
2016-02-24,水,8499
2016-02-25,木,7990
2016-02-26,金,8043
2016-02-27,土,4414
2016-02-28,日,7175
2016-02-29,月,9767
2016-03-01,火,5510
2016-03-02,水,8564
2016-03-03,木,8151
2016-03-04,金,10105
2016-03-05,土,9250
2016-03-06,日,4957
2016-03-07,月,5223
2016-03-08,火,8381
2016-03-09,水,8446
2016-03-10,木,8249
2016-03-11,金,8132
2016-03-12,土,6580
2016-03-13,日,4186
2016-03-14,月,8275
2016-03-15,火,6987
2016-03-16,水,8093
2016-03-17,木,7459
2016-03-18,金,16445
2016-03-19,土,6219
2016-03-20,日,4994
2016-03-21,月,2326
2016-03-22,火,7731
2016-03-23,水,4978
2016-03-24,木,12151
2016-03-25,金,10255
2016-03-26,土,8202

これでデータの取得と整形が完了しました。


最後に、取得したデータを可視化していきます。

データビジュアライゼーションのライブラリでは「D3.js」が有名ですが、今回は「D3.js」から派生した「C3.js」というライブラリを試してみます。 try_001_11


C3.js」は「D3.js」より自由度は少ないですが、さくっと実装できる強みがあります。

C3.js」利用するには「C3.js」のJSファイルとCSSファイルが必要になります。あと、「D3.js」のJSファイルも必要になりますのでダウンロードします。


それではスクレイピング処理で作成した「Walking.csv」を使ってチャートを実装してみます。


index.html


<!DOCTYPE html>
<html lang="ja">

<head>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="keywords" content="node.js,C3.js,歩数,チャート">
    <meta name="description" content="歩数チャート">
    <meta name="author" content="dayjournal">
    <title>歩数チャート</title>

    <!--D3.js読み込み-->
    <script src="./Library/d3/d3.min.js" charset="utf-8"></script>
    <!--C3.js読み込み-->
    <script src="./Library/c3/c3.min.js"></script>
    <link href="./Library/c3/c3.css" rel="stylesheet" />

</head>

<body>

    <div id="chart"></div>
    <script src="./Walking.js"></script>

</body>

</html>

Walking.js


//C3.js宣言
var chart = c3.generate({
    data: {
        //外部ファイル設定
        url: './Walking.csv',
        //チャート形式設定:バーチャート
        type: 'bar'
    }
});

//10秒後にドーナツチャートに変化
setTimeout(function () {
    chart.transform('donut');
}, 10000);

index.htmlを実行すると下記のようにバーチャートが表示されます。 try_001_09


10秒後にドーナツチャートに変化します。 try_001_10


example


今回はNode.jsとC3.jsを利用して歩数情報をスクレイピングして可視化してみました。数字だけでは見えづらいものを可視化してみると見えてくるものがあります。

取得した歩数データを、曜日別に集計してみました。チャートを確認してみると、どうやら土曜日に一番歩いていることがわかりました。平日は通勤で1時間程度歩いているので、土曜日は平日の時間よりもさらに歩いていることになります。おそらく休みの日は比較的土曜日に活動して、日曜は少し休み気味なのでしょう。歩数が一番少ないのは水曜日ですが何故だろう。もっと細かく分析すれば要因を気見つけれそうですね。季節別だったり抽出方法を変えてもっと色々な分析ができそうです。



book

Q&A