Google 試算表 (7) - 簡單的股票爬蟲 ( Node.js )
當我們已經熟練了寫入資料到試算表,接著就可以用 Node.js 玩點好玩的東西,過去有寫過網路爬蟲的文章,但並沒有一個妥善的資料儲存空間,現在有了 Google 試算表後,理所當然的可以用它來記錄爬到的資料,這裡就用 Node.js 抓取當天的股票收盤資料,並及時存到 Google 試算表裡頭。
之前寫的文章:做個簡單的爬蟲 ( 幣值、空污 PM2.5 )
股票資料來源
股票資料來源最好的地方就是「台灣證卷交易所」,當中提供了非常多的資料讓我們使用,因為是用 Node.js 來抓,所以也就不用考慮瀏覽器跨域的問題。打開台灣證卷交易所的網頁之後,選擇「交易資訊 > 盤後資訊 > 個股日成交資訊」。
打開網頁開發者工具,頁籤選擇 Network,在上方股票代碼處,填入對應的股票代碼,按下查詢,這時候應該可以看到 Network 的資訊中出現了一個 json 的資料格式,這就是這支股票的成交資訊,也是待會我們會用到的。
這個 json 的網址有固定的格式,date=20170621
表示日期,stockNo=2330
表示這支股票編號,後面那串 &_ 不知道做什麼用的 ( 刪掉好像沒影響所以就刪掉吧 :p ),了解格式之後待會我們就會把想要抓取的股票代碼,動態的塞入網址內,就可以取得對應的資料了。
www.tse.com.tw/exchangeReport/STOCK_DAY?response=json&date=20170621&stockNo=2330&_=1498059436321
Google App Script
有了資料來源之後,接著 Google App Script 儲存資料的部分,一開始放入五個變數,分別是試算表網址、工作表名稱、股票代碼、股票名、收盤價,因為傳進去的只能是字串,所以要用逗號分割成陣列,然後透過getRange
指定寫入的範圍,並用setValues
寫入資料。。
完成後記得部署到網路上,權限設定為「任何人,甚至是匿名使用者」
function doGet(e) {
var params = e.parameter;
var sheetUrl = params.sheetUrl; // 試算表網址
var sheetName = params.sheetName; // 工作表名稱
var num = params.num; // 股票代號
var name = params.name; // 股票名稱
var price = params.price; // 收盤價
var numArr = num.split(','); // 將代號分割成第 1 列陣列
var nameArr = name.split(','); // 將名稱分割成第 2 列陣列
var priceArr = price.split(','); // 將收盤價分割成第 3列陣列
var arr = [numArr,nameArr,priceArr];
var SpreadSheet = SpreadsheetApp.openByUrl(sheetUrl);
var SheetName = SpreadSheet.getSheetByName(sheetName);
var range = SheetName.getRange(1,2,3,numArr.length);
range.setValues(arr); // 存入資料
return ContentService.createTextOutput(true);
}
完成之後可以寫個 debug.gs 來測試,填入試算表網址與工作表名稱,隨便寫個股票名稱、代號與金額,記得都是用字串的格式,並且在後方加個逗號,如果不要有逗號,就要在 doGet 多些判斷,以下面的例子來說,執行後應該就可以看到有三列的資料寫入到第二欄,這樣我們就完成基本的 Google App Script 和 Google 試算表設定。
記得試算表的權限要設定為「知道連結的使用者都可以編輯」。
function debug() {
doGet(
{
"parameter":{
sheetUrl:'試算表網址',
sheetName:'工作表名稱',
name:'台積電,',
num:'2330,',
price:'215,'
}
}
);
}
Node.js
開始之前要先用命令輸入視窗,進入程式的資料夾,用 npm 安裝 request 模組,透過這個模組來載入網頁、json 或 xml 檔案,並且也可以發送 GET 或 POST 的指令。
參考:request npm
var request = require("request");
接著因為股票的 JSON 網址裡面有日期,所以直接用當前的日期轉換即可,不過因為網址的日期如果是個位數都會補零,所以要多做個判斷式來補零。
var date = new Date();
var y = date.getFullYear();
var m = date.getMonth() + 1;
var d = date.getDate();
if (m < 10) {
m = '0' + m;
}
if (d < 10) {
d = '0' + d;
}
var today = y + '' + m + '' + d;
再來看到主結構,一開始我們可以把想要抓取的股票代碼做個陣列,然後設定 stockIndex 為 0,表示從陣列的第一個開始抓,再來就是寫個 run 的流程,裡頭把股票代碼與日期放入網址內,在執行 run 的時候會做判斷,如果有 stock 的數值,在進行完 request 之後,就會在執行一次並抓取下一個網址的資料,如果每個陣列都處理完,就進行下一個 sheet 的流程。
這邊有多寫一個判斷式,判斷body.indexOf('html') != -1
,因為在抓取的過程中,有時候會遇到抓不到資料報錯,產生抓到的資料是 404 網頁而造成流程當掉,透過這個判斷,在沒有資料的時候重抓,就可以確保不會有問題發生了。
var stock = [2454, 2317, 2002, 2330, 2412];
var stockIndex = 0;
var num = '',
name = '',
price = '';
var run = function() {
var stockNo = stock[stockIndex];
var jsonUrl = "http://www.twse.com.tw/exchangeReport/STOCK_DAY?response=json&date=" + today + "&stockNo=" + stockNo;
if (stockNo) {
console.log(stockNo);
request({
url: jsonUrl,
method: "GET"
}, function(error, response, body) {
if (error || !body) {
return;
} else {
// 如果沒有資料,會出現 404 的 html 網頁,此時就重新抓取
if (body.indexOf('html') != -1) {
console.log('reload');
run2();
} else {
b = JSON.parse(body);
var json = b.data;
var title = b.title.split(' ');
var data = json[json.length - 1];
num = num + title[1] + ',';
name = name + title[2] + ',';
price = price + data[data.length - 3] + ',';
stockIndex = stockIndex + 1;
run();
}
}
});
} else {
console.log(num);
console.log(name);
console.log(price);
sheet();
}
};
再來就是 sheet 的流程,裡面設定一個變數 parameter,就是我們要存入試算表的變數,跟剛剛的 debug.gs 有異曲同工之妙,有了這個變數之後,再緊接著另外的 request,比較需要注意的是要使用qs
的屬性來傳遞變數。
var sheet = function() {
var parameter = {
sheetUrl: '試算表網址',
sheetName: '工作表名稱',
num: num,
name: name,
price: price
}
request({
url: 'Google App Script 網址',
method: "GET",
qs: parameter
}, function(error, response, body) {
console.log(body);
});
}
run();
最後就是執行 run 的流程,這樣當我們回到命令輸入的視窗,輸入node js名稱
,應該就可以看到試算表內順利的存入抓到的股票資訊了。
小結
過去在沒有資料庫的情況下,大多都是用個網頁來呈現,現在透過 Google App Script 和 Google 試算表的搭配,我們就可以更簡便的儲存與分析資料囉。