LINE Bot聊天機器人程式開發教學(五):使用Promise風格的request-promise讀取資料開放平臺的天氣品質JSON資料

這篇文章跟Line聊天機器人程式沒有直接關係。本文將說明如何使用Node.js的request以及request-promise模組,讀取行政院環境保護署,環境資源資料開放平臺空氣品質指標(AQI)數據,下一篇文章再將它整合到Line聊天機器人程式。

request模組是個簡單易用的HTTP請求前端物件,從它的專案介紹網頁的第一個範例,可知它有多簡單:

使用request模組連線google首頁

當request接收到伺服器的回應時,回呼函式將被觸發,並把回應內容傳給回呼函式的body參數

我們已經在「LINE Bot聊天機器人程式開發教學(四):佈署與測試Echo Bot」這篇文章的package.json檔設定安裝request模組,假設此程式檔名為req.js,執行結果如下,它將傳回Google首頁的HTML內容:

request傳回Google首頁的HTML內容

即時空氣品質指標的JSON格式數據位於這個網址:http://opendata2.epa.gov.tw/AQI.json

使用request讀取該JSON數據的範例程式如下,request的第一個參數改成名叫opts的自訂物件,請求網址放在uri屬性、另一個json屬性要設成true

opts自訂物件

此程式將在終端機顯示所有空氣檢測站的數據:

在終端機顯示所有空氣檢測站的數據

空氣品質指標AQI數據JSON格式

環境資源資料開放平臺的空氣品質指標,把所有觀測站的資料包在一個陣列中,底下是其中一個位於「臺中市」的「西屯」觀測站的數據:

「臺中市」的「西屯」觀測站的JSON數據

底下的request程式將搜尋並取出JSON資料裡的「西屯」觀測站數據:

取出JSON資料裡的「西屯」觀測站數據的request

完整的程式碼如下:

var request = require('request');

const SITE_NAME = '西屯';
const opts = {
    uri: "http://opendata2.epa.gov.tw/AQI.json",
    json: true
};

request(opts, function (error, response, body) {
    if (!error && response.statusCode === 200) {
        let data;
        
        for (i in body) {
            if (body[i].SiteName == SITE_NAME) {
                data = body[i];
                break;
            }
        }

        console.log(data)
    }
});

程式執行結果:

使用Promise處理非同步程序

上文的request模組使用「回呼函式」執行非同步任務。「LINE Bot聊天機器人程式開發教學(三):建立Node.js版的Echo Bot」這一篇文章使用的Line程式庫則採用稱為Promise(直譯為「承諾」)的非同步程式設計風格。這兩種非同步程式語法的差別在於,一個是把回呼函式嵌套在處理函式裡面(如下圖左),另一個則是用“.then”指令,串連回應函式(註: “then”有「然後…」之意):

兩種非同步程式比較

假設我們要撰寫一個「擷取網路資料、整理收到的資料之後,寫入磁碟檔案」這樣的程式,由於擷取網路和寫入檔案,都需要非同步處理,而且要依照順序執行兩大步驟,用「回呼函式」寫成的架構如下:

回呼函式

使用Promise風格的寫法則是:

Promise風格

回呼函式和Promise程式都能分成數行來寫:

分成數行的回呼函式和Promise程式

隨著函式增加、程式變得複雜,層層嵌套的回呼函式將導致程式不易閱讀和管理,老外用“callback hell”(回呼地獄)來形容這種情況。雖然程式功能一樣,但Promise風格的程式碼更清晰。Promise也被納入ECMAScript 6語言標準

使用request-promise模組向伺服器請求資料

request-promise模組是基於request模組,改用Promise風格的HTTP請求前端物件,程式基本架構如下:

Promise風格的程式基本架構

就像海誓山盟的諾言,承諾有可能無法兌現,執行Promise(承諾)敘述若成功的話,”then()”當中的回應函式就會被執行;若承諾無法履行,則執行“catch()”裡的函式。

海誓山盟

改用request-promise模組取得「西屯」觀測站空氣品質指標數據的完整程式碼如下:

var rp = require('request-promise');

const SITE_NAME = '西屯';
const opts = {
    uri: "http://opendata2.epa.gov.tw/AQI.json",
    json: true
};

rp(opts)
.then(function (repos) {
    let data;
    
    for (i in repos) {
        if (repos[i].SiteName == SITE_NAME) {
            data = repos[i];
            break;
        }
    }
    console.log(data);
})
.catch(function (err) {
    console.log('出錯了~找不到指定資源…');
});

延伸閱讀

Posts created 468

17 thoughts on “LINE Bot聊天機器人程式開發教學(五):使用Promise風格的request-promise讀取資料開放平臺的天氣品質JSON資料

  1. 老师,您好 我还是想问下 超图解Arduino互动入门设计 中用dht11远程监控温湿度值 中的硬件连线问题 我对其中以太网卡的使用不太清楚 我用学校的宽带线插在以太网卡上 然后让笔记本用手机热点上网 无论怎么打网页 网页都打不开192.168.1.25这个网址 想问下老师 知不知道是哪里的原因 如果我表述的不清楚 我之后给老师您发图片 万望老师百忙之中抽空解答 不胜感激!

    1. 从笔记本开放热点给其他设备上网,这些连接设备所处的网路和你的笔记本分属于不同的局域网,所以不能互连。而且那些连接笔记本热点的装置,估计都是用DHCP,不是固定IP。

      若要测试连线,你可以用另一个装置(例如:手机),连上同一个笔记本的热点,这样一来,手机和Arduino就位于相同网域,可以相互连接。

      假设你用的是Windows 10,从「行动热点」画面就能看到目前连接的装置名称和IP地址。

      thanks,
      jeffrey

  2. 老師您好:

    我輸入heroku open 會跳出瀏覽器也有看到Hello World!
    可是輸入node req.js 卻出現
    ‘node’ 不是內部或外部命令、可執行的程式或批次檔

    請問是為什麼?

  3. 老師你好,我在執行的時候都會出現亂碼的問題
    後來我把編碼調成UTF-8格式,但不管是哪個地區都只會顯示 ” 出錯了~找不到指定資源… ”
    還有甚麼地方是我遺漏掉的嗎@@?

    1. 欸…我在windows 10系統系統的命令列執行都沒有問題,請直接把程式佈署到heroku再測試。

      thanks,
      jeffrey

  4. 老師您好
    目前想用line chatbot接收使使用者傳的訊息
    並存到monglab中但程式一直跑不過
    //————————————————–
    bot.on(‘message’, function(event) {
    if (event.message.type === ‘text’) {
    var msg = event.message.text;
    // 將聊天資料轉成資料模型
    var m = new Message(msg);
    // 存至資料庫
    m.save(function (err) {
    if (err) {
    console.log(err)
    }else
    console.log(‘success’)
    });
    }
    });
    //————————————————–
    請問在主要程式部分這樣打有錯嘛!?

  5. 老師您好:
    輸入node req.js 卻出現
    ‘node’ 不是內部或外部命令、可執行的程式或批次檔
    但我的電腦系統有安裝node.js

  6. 請問輸入node req.js 出現
    $ node req.js
    internal/modules/cjs/loader.js:638
    throw err;
    ^

    Error: Cannot find module ‘request’
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15)
    at Function.Module._load (internal/modules/cjs/loader.js:562:25)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object. (C:\Users\405_2\desktop\linebot_basic\linebot\req.js:1
    :15)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    是哪邊出了問題嗎?

    1. 請問要如何安裝呢 試過很多方法都不行
      由老師解答 謝謝

  7. 老師您好 在輸入node req.js出現
    D:\tcim\heroku\linebot>node req.js
    internal/modules/cjs/loader.js:888
    throw err;
    ^

    Error: Cannot find module ‘D:\heroku\linebot\req.js’
    [90m at Function.Module._resolveFilename (internal/modules/cjs/loader.js:885:15)[39m
    [90m at Function.Module._load (internal/modules/cjs/loader.js:730:27)[39m
    [90m at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)[39m
    [90m at internal/main/run_main_module.js:17:47[39m {
    code: [32m’MODULE_NOT_FOUND'[39m,
    requireStack: []
    }

    請問該如何解決呢

    1. 錯誤訊息’MODULE_NOT_FOUND’代表找不到程式用到的模組,請先安裝再執行。

      thanks,
      jeffrey

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top