工作中遇到一個情境是,打 API 可以要到當下的資訊,但 User 想要幾分鐘之內的紀錄,如果要做報表的話,就要非常手動的用 postman 打 API N 次,然後再將儲存下來的 json 轉成 csv,最後再將 n 個 csv 合成一份檔案。實際若是每分鐘打一次 API,假設只抓十分鐘,就是存十次 json 檔案,再將十個 json 檔案轉成十個 csv ,最後再手動合成一個 csv 檔案,光是手動的時間絕對超過三十分鐘。為此,我決定要寫一隻程式讓上面的所有動作自動化。

環境使用 NodeJS。

安裝套件

共安裝 3 個套件

  • axios: 戳 API 用
  • moment: 處理時間
  • json2csv: 將 data 轉成 csv 格式
1
yarn add axios moment json2csv

程式解說

這裡只需要寫一隻 getData.js 就搞定 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const axios = require('axios');
const moment = require('moment');
const { Parser } = require('json2csv');
const fs = require('fs');

let head = []; // 存取 head 資料
...
let timer = null; // 定時抓取資料的計時器
let duration = 60000; // 多久抓一次資料
let token = ''; // 存取 API token
let apiURL = 'put API URL';
let appID = 'put API ID';
let appSecret = 'put API Code';
let startTime = moment().format(); // 紀錄開始時間
let grabMiniute = 3; // 設定要抓取的分鐘數
let endTime = moment(startTime).add(grabMiniute, 'minutes'); // 計算結束的時間

// 取得 API token
async function getToken() {
let res = await axios({
methos: 'GET',
url: `${apiURL}/api/auth?appId=${appID}&appSecret=${appSecret}`
});
token = res.data.data;
}

// 將戳 API 包裝成一個程式,若 token 過期會重戳 API
async function requestAPI(apiMethod, apiURL, retryFunc) {
try {
return await axios({
method: apiMethod,
url: apiURL,
headers: {
AccessToken: token,
System: appID
}
});
} catch(error) {
if (error.message.indexOf('401') > -1) {
// get token
await getToken();
retryFunc();
}
}
}

// 戳 head API 取資料
async function getHead() {
let res = await requestAPI('GET', `${apiURL}/api/Head`, getHead);
if (res) { head = head.concat(res.data); }
console.log('head', head.length);
}

...

// 一次取得多種資料
async function getAllData() {
// get token
await getToken();

console.log(`----- ${grabMiniute} minutes left -----`);

// grab data
getHead();
getFeeder();
getNozzle();

grabMiniute = grabMiniute - 1;
}

// 寫檔案
function writeFile(data, fileName) {
fs.writeFile(fileName, data, 'utf8', function (err) {
if (err) {
console.log(`An error occured while writing ${fileName}.`);
return console.log(err);
}

console.log(`${fileName} has been saved.`);
});
}

// 轉格式並儲存檔案
function saveFile() {
try {
// 將 json 轉 csv
const fieldsHead = [columnA, ...];
const fieldsFeeder = [columnA, ...];
const fieldsNozzle = [columnA, ...];
const parserHead = new Parser({ fieldsHead });
const parserFeeder = new Parser({ fieldsFeeder });
const parserNozzle = new Parser({ fieldsNozzle });
const csvFeeder = parserFeeder.parse(feeder);
const csvHead = parserHead.parse(head);
const csvNozzle = parserNozzle.parse(nozzle);

console.log(`----- SAVE FILES -----`);
writeFile(csvHead, 'Head.csv');
writeFile(csvFeeder, 'Feeder.csv');
writeFile(csvNozzle, 'Nozzle.csv');
} catch (err) {
console.error('saveFile ERROR: ', err);
}
}

/* ---------------
Start from here
---------------*/
// 一開始先執行
getAllData();

// 啟動計時器
timer = setInterval(() => {
let now = moment().format();
if(moment(now).isBefore(endTime)) {
// 如果還沒到結束時間,就繼續取資料
getAllData();
} else {
// 如果時間到就停止計時器
clearInterval(timer);
// 將資料儲存成檔案
saveFile();
}
}, duration);

跑起來吧~

1
node getData.js

跑起來吧

後記:其實 User 一直不停的找我手動抓資料,一開始邊寫程式還要先手動抓資料很阿砸,但後來工具完成後,變成非常順手,覺得幸好有寫這隻小工具的感覺XD