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寫入資料。。

完成後記得部署到網路上,權限設定為「任何人,甚至是匿名使用者


參考:Google 試算表 (5) - 欄和列的插入與刪除

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 試算表的搭配,我們就可以更簡便的儲存與分析資料囉。

有興趣瞧瞧其他新文章嗎?