【データの集め方講座】Node.jsを使って株データを収集, MySQLに保存
はじめに
ごあいさつ
ご高覧いただきありがとうございます.
ソフトウェアエンジニアのKitaharaです.
本日はNode.jsを使った株式情報の収集方法を解説します!
==================================================
今回の記事は下記の記事のNode.jsの実装になります.
==================================================
株式情報を取得するメリット・取得方法
株式を取得すると何がいいのか
今回のテーマの株式ですが, 収集してみたいという方も多いのではないでしょうか?
ですが, 株式がどんなものか知らない方もいらっしゃると思うので, まずは株式が何かを紹介してから株式情報を収集することの有用性についてお話いたします.
株式の定義ですが, SMBC日興証券のサイトに用語集があり, 掲載されていましたので引用させていらだきます.
この用語集から分かる通り, 成長すると思った会社の株式を買ってその後会社が成長すると株式の値段が上がって売却益を得ることができます. 多くの人が株式情報を収集したいとなる理由はおそらく株式データを使って分析をし, 高騰しそうな株を見つけたいからではないでしょうか?
==================================================
株式情報をスクレイピングで取得するのは難しい
しかしながら, 現在日本において株式情報を取得することは難しいです.
その理由は多くの証券会社・株式情報サイトでスクレイピングが禁止されているからです.
例えばYahooファイナンスでは以下のように記載がされています.
理由はここに書かれている通り, システムに負荷がかかるからです.
株式情報を欲しい人(特にリアルタイム)だと1秒間に何回もリクエストを送ることが想定されますので, 負荷がかかってしまうのです. そのため, 他のサイトでもスクレイピングが禁止されていることが多いです.
補足ですが, そもそも大量のリクエストによってによってサーバーに負荷をかけること自体迷惑行為(Dos攻撃とみなされることもあります)ですのでやらないように気を付けるべきです. もしリクエストを何回も投げることになる場合は専用のモジュール等で時間をかけるように調整し, サイト制作者様に迷惑をかけないようにしましょう.
なお, 今回利用する無尽蔵に関しては規約上問題がなく, かつリクエストの回数も一日一回で済むのでサイト制作者様に迷惑をかけることはないです.
==================================================
どのようにして取得するのか
今回は以下の流れでデータを取得し, 保存します.
(1) サイト無尽蔵から当日のデータをzipファイルで取得
(2) zipファイルを解凍し, CSVファイルを別ディレクトリに保存
(3) zipファイルを削除
(4) データをMySQLに格納
(5) csvファイルを削除
無尽蔵の特徴としてデータをCSVファイルで配布しているということがあります. CSVファイルの取得には一回のリクエストで済むわけですから実行時間が短いです.
作るものの説明
当日の株式データをcsvファイル形式で取得するプログラムとcsvファイルに記述されたデータをMySQLにアップロードするプログラムを作成します.
使用するものの説明
色々なデータを掲載しているデータサイト
CSVファイル形式で当日の株価データを取得することができます
管理人によるとデータの更新が確約されているわけではないです
Node.js
JavaScriptをサーバーサイドでも実行できるようにするもの
MySQL
環境構築
言語のバージョン
Node.js のバージョンとmysqlのバージョンです.
OSはUbutntu 20.04を使っています.
~/KabuBot$ node --version
v14.18.0
mysql> select version();
+-------------------------+
| version() |
+-------------------------+
| 8.0.28-0ubuntu0.20.04.3 |
+-------------------------+
1 row in set (0.01 sec)
ディレクトリの構成
適宜ファイルを作成してください.
(ファイルの名前は必ず下記のようにする必要はありません.)
最終的なディレクトリ構成がこのようになっていれば大丈夫です.
.
├── csv
├── download.js
├── extract_file.js
├── node_modules
├── package-lock.json
├── package.json
└── zipfile
テーブルの作成
利用するデータベースとテーブルを作成しておきましょう.
(私はDB名はkabuにしました.)
mysql> create table YOUR_DATABASE_NAME.stocks (
id int AUTO_INCREMENT not null PRIMARY KEY,
date date not null,
stock_name varchar(50) not null,
stock_code int not null,
open float not null,
high float not null,
low float not null,
close float not null,
volume float not null,
market varchar(10) not null
);
プログラムの作成
INPUT (download.js)
var request = require('request');
const fs = require('fs');
var unzipper = require('unzipper');
console.log('library has been loaded!');
var dt = new Date();
var year = dt.getFullYear();
var month = ("0" + (dt.getMonth()+1)).slice(-2)
var date = ("0" + dt.getDate()).slice(-2);
var year_last_two_degits = String(year).substr(2,2);
const zip_file_name = 'T'+year_last_two_degits+month+date+'.zip';
const csv_file_name = 'T'+year_last_two_degits+month+date+'.csv';
var url = 'http://mujinzou.com/d_data/'+year+'d/'+year_last_two_degits+'_'+month+'d/'+zip_file_name;
var zip_file_path = './zipfile/kabu_data.zip';
var csv_dir_path = './csv';
var csv_file_path = csv_dir_path+csv_file_name
console.log('vars have been loaded!');
const rqt = (target_url) => {
return new Promise((resolve, reject) => {
request(
{method: 'GET', url: target_url, encoding: null},
function (error, response, body){
if(!error && response.statusCode === 200){
fs.writeFileSync(zip_file_path, body, 'binary');
fs.createReadStream(zip_file_path)
.pipe( unzipper.Extract({ path: csv_dir_path }) )
resolve()
}
}
);
})
}
(async () => {
const body = await rqt(url)
console.log('Done!');
})()
INPUT (extract_file.js)
var mysql = require('mysql');
const fs = require('fs');
const csv = require('csv');
const iconv = require('iconv-lite');
const encoding = require('encoding-japanese');
console.log('library has been loaded!');
// MySQLの設定
var connection = mysql.createConnection({
host: 'localhost',
user: 'your_user_name',
password: 'your_password',
database:'kabu'
});
var csv_dir_path = './csv';
var csv_file_path = csv_dir_path+csv_file_name
const parser = csv.parse((error, data) => {
//ループしながら1行ずつ処理
data.forEach((element, index, array) => {
connection.query(
'INSERT INTO stocks (date, stock_name, stock_code, open, high, low, close, volume, market) VALUES (?,?,?,?,?,?,?,?,?)',
[
element[0],
String(element[3]).substr(0,5),
element[1],
element[4],
element[5],
element[6],
element[7],
element[8],
element[9]
],
(error, results) => {
if (error) {
console.log(error)
return
}
console.log(results)
return
}
);
})
connection.end();
});
var buf = fs.readFileSync(csv_file_path);
fs.readFile(csv_file_path, function(err, data){
if (err) throw err;
var buf = new Buffer.from(data, 'binary');
var retStr = iconv.decode(buf, "Shift_JIS"); //作成したバッファを使い、iconv-liteでShift-jisからutf8に変換
fs.writeFile(fixed_csv_file_path,retStr,(error)=>{
console.log('処理データをCSV出力しました。');
});
fs.createReadStream(fixed_csv_file_path).pipe(parser);
});
INPUT (TERMINAL)
~/KabuBot$ node download.js
~/KabuBOt$ node extract_file.js
mysql> use YOUR_DATABASE
mysql> select * from your_table
プログラムの解説
download.js
最初のurlを設定する部分の解説です.
var dt = new Date();
var year = dt.getFullYear();
var month = ("0" + (dt.getMonth()+1)).slice(-2)
var date = ("0" + dt.getDate()).slice(-2);
var year_last_two_degits = String(year).substr(2,2);
const zip_file_name = 'T'+year_last_two_degits+month+date+'.zip';
const csv_file_name = 'T'+year_last_two_degits+month+date+'.csv';
var url = 'http://mujinzou.com/d_data/'+year+'d/'+year_last_two_degits+'_'+month+'d/'+zip_file_name;
無尽蔵のURLは
year: 年
month: 月(常に2桁)
day: 日付(常に2桁)
year_last_tow_digits: yearの下二桁
とするときに
"http://mujinzou.com/d_data/"+year+"d/"+year_last_two_digits+"_"+month+"d/T"+year_last_two_digits+month+day+".zip"
となっています. これを作るためにDate型のインスタンスを作成しています.
下記の部分は不思議に感じる方がいるかもしれませんが, これは1月が0と表示される使用のためです.
var month = ("0" + (dt.getMonth()+1)).slice(-2)
node.jsの日付処理が気になる方は以下の記事を参考にしてみてください.
次にデータをダウンロードする部分です.
const rqt = (target_url) => {
return new Promise((resolve, reject) => {
request(
{method: 'GET', url: target_url, encoding: null},
function (error, response, body){
if(!error && response.statusCode === 200){
fs.writeFileSync(zip_file_path, body, 'binary');
fs.createReadStream(zip_file_path)
.pipe( unzipper.Extract({ path: csv_dir_path }) )
resolve()
}
}
);
})
}
(async () => {
const body = await rqt(url)
console.log('Done!');
})()
requestメソッドでHTTPリクエストを投げ, zipファイルをダウンロードします. zipファイルはそのまま使えないので解凍して中身をcsv/に保存します.
extract_file.js
mysqlのコネクション設定
// MySQLの設定
var connection = mysql.createConnection({
host: 'localhost',
user: 'your_user_name',
password: 'your_password',
database:'kabu'
});
作成したテーブルにアクセスできるか試してみましょう.
動かない場合は以下のことを確認してみてください.
MySQLは起動しているか?
起動していないときはsystemctlなりserviceなりで起動してみましょう
ユーザー名とパスワードは正しいか
MySQLにログインできるか試すとよいです
DB名はあっているか
作ったDB内にテーブルがあるか show tables; で確認しましょう
次にデータを登録する部分を見ていきます.
データの登録はcsvファイルを読み込んだ段階で行います.
const parser = csv.parse((error, data) => {
//ループしながら1行ずつ処理
data.forEach((element, index, array) => {
connection.query(
'INSERT INTO stocks (date, stock_name, stock_code, open, high, low, close, volume, market) VALUES (?,?,?,?,?,?,?,?,?)',
[
element[0],
String(element[3]).substr(0,5),
element[1],
element[4],
element[5],
element[6],
element[7],
element[8],
element[9]
],
(error, results) => {
if (error) {
console.log(error)
return
}
console.log(results)
return
}
);
})
connection.end();
});
Insert 文の Valuesの部分ですが, 使用する言語によって変わることがあります. (Pythonでの実装だと%sを書いています)
また, 最後のconnection.end()を書かないと処理が終了しないので書き忘れないようにしてください.
おわりに
今回はNode.jsとMySQLを使って株式情報を取得し保存する方法を解説しました!参考になったという方はぜひハートボタンを押していってください!
モチベーションが上がります!
記事内で不明な点等ございましたら気軽にご連絡ください.
Twitter: @kitahara_devemail: kitahara.main1@gmail.com
参考文献
この記事が気に入ったらサポートをしてみませんか?