[Tool Note] — 關於爬蟲#2

Introduction & 前言

繼上一篇爬蟲之後這次決定繼續研究爬蟲,一方面當興趣玩,一方面了解 LINE 的機器人及繼續了解 Node.js。這次一樣是透過上次完成的爬蟲機器人將日幣資訊傳送至 LINE 上,文章將不會過多涉略到太難的 LINE API


事前準備

一樣需要 node.js 需要至少 4.x 版本以上,不過現在官方網站都能下載到最新的,所以還沒安裝到官方網站下載就可以囉,傳送門

安裝完成

-> 安裝的時候會一併安裝 npm,不曉得這是什麼可以點我看我的文章介紹,[Tool Notes] — 關於Webpack

關於爬蟲的套件可以參考 [Tool Notes] — 關於爬蟲


目錄

  1. LINE Bot 基本介紹

  2. LINE Bot 申請

  3. LINE Bot Webhook 伺服器 建置

  4. LINE Bot 與日幣爬蟲應用

本文章如果有任何錯誤之資訊敬請不吝嗇告知,也歡迎理性的切磋指教,非常感謝各路大神。


LINE Bot 基本介紹

基本概念說明

LINE BOTLINE 其中一個服務,原為『LINE@生活圈』或『LINE官方帳號』,不過 2019年4月18日 起,已經整合為 LINE官方帳號2.0,且原本的 LINE@生活圈 免費帳號付費帳號 都會在 2019年10月1日 前完成帳號的自動升級。

有興趣可以參考此篇文章 迎接 LINE 官方帳號 2.0 全面升級:到底有哪些事情改變了?

通常 LINE BOT 就像是你會在外面加的官方帳號,你需要加入他為好友,才能與之互動。

為什麼現在越來越多人會使用 LINE Bot 呢?簡單說就是任何使用者及商家都能從0元無料上手,後台功能全部都開放,甚至API串接無需審核即可使用,現今不一定每一個人每天都會用 FaceBook 但是有使用智慧型手機的人幾乎都離開不通訊軟體,而台灣及日本就是 LINE 的最大使用客群。

LineBot

基本架構說明

基本上如果沒有透過 LINE API 回應的話,就是從 LINE 的後台直接去做設定,就不會有 Client Server 這塊,那我們要做的事情很簡單,就是申請一個 LINE Bot 然後建立我們的 Client Server

大致上流程就是 LINE Users 會發起要求或回應,然後 LINE Server 會判斷要傳送訊息給哪一個 LINE Bot,並解確認是否有起用 Webhook URL 並且有設定 Webhook 網址目標,如果沒有就不會傳送訊息給任何伺服器。

如果有設定 Webhook 目標網址LINE Server 會將 LINE Users 的使用者訊息,傳送一個 HTTP POST Resquest,在 Resquest 中會定義使用者的訊息類型。

*其中在 LINE Server 轉傳訊息時,會將 LINE BotChannel Secret 進行 SHA-256(註1) 加密並附加在 HTTP POST ResquestHeaderX-Line-Signature 欄位,讓 Client Server 收到訊息可以驗證此欄位,證明訊息發送者身份是不是真的 LINE Server。*

每一個 LINE Bot 都有他自己的 頻道識別編號(Channel ID),用來區分不同的聊天機器人,我們有兩種方式來管理我們的機器人,一種就是上面提到的 LINE 後台,一種即是我們要提到的 Messaging API

簡單說會用到的就是 Channel IDChannel Secret(頻道密鑰) 及 Channel access token(頻道存取令牌),最後還有 UserId 這四種。

這邊並不是一般使用者自行設定的ID 是一組30位數的英數字混合字串 不同 LINE Bot 將會有不同的 UserID

最後在 Client Server 我們需要建立一個 Web API 服務的伺服器,接著收到不同類型的使用者訊息之後,需要回應(Response)HTTP OK 的狀態碼(ex:HTTP Status Code:200 註2)給 LINE Server 伺服器確認。

Client Server 就是我們可以設計程式的地方,可以把我們的爬蟲放這邊,透過 Messaging API 我們可以傳送各式各樣的訊息,譬如文字、圖片、選單…等等。

大致架構

註1: SHA256是SHA-2下細分出的一種演算法。SHA-2,名稱來自於安全雜湊演算法2(英語:Secure Hash Algorithm 2)的縮寫,一種密碼雜湊函式演算法標準,由美國國家安全域性研發,屬於SHA演算法之一,是SHA-1的後繼者。詳細可參考 SHA256演算法原理詳解

註2: 200為請求成功,相關代碼查詢可以參考 HTTP 狀態碼


LINE Bot 申請

LINE Bot 基本介紹

根據申請人的資格分為兩種帳號『一般帳號』與『認證帳號』還有最後一種是無法自行申請的『LINE企業帳號』,差別在下方。

  1. 一般帳號
    普遍人物都能申請,但是發送的訊息內容需要遵循法規以及LINE官方帳號使用條款
  2. 認證帳號
    政府立案合法經營的客戶,要符合審核條件即可成為認證帳號
  3. 企業帳號

-> 透過LINE的邀請,通常會是在LINE上深度經營的帳戶。

Line差別

關於收費不再依照付費區分訊息使用量,改採用訊息發送數量,就是像發簡訊一樣,分成三種用量『低用量』、『入門版』、『進階版』三種,值得一提的是 2.0版本 沒有了好友的上限。

收費方案

LINE Bot 帳號申請

廢話說了一堆就開始拿起你的釣竿吧!首先我們要先申請一組 LINE Bot 帳號,進入 LINE for Business ,我們會申請一個低用量方案的 Messaging API

點選右上方的『線上申請官方帳號』,進入後會要求登入 LINE帳號,或直接進入『LINE Official Account Manager』進入後點選 建立LINE官方帳號

點選 使用LINE帳號登入

基本上填寫『帳號名稱』、『電子郵件』與選擇『業種』,點擊確認之後再確認一次資訊然後點擊『提交』送出。

LINE官方帳號是隨機ID,如果想購買專屬ID,可以到 LINE Official Account Manager 的『設定』->『帳務專區』->『專屬ID』。

LINE Messaging API 啟用

LINE Official Account Manager 頁面左上角區域,可以看到目前官方帳號資訊。

點擊左上角 回應模式 再點左側 Messaging API

中間會有 狀態 未使用,點擊下方按鈕 啟用Messaging API,接著會跳出選擇提供者,目的在於讓企業或使用者管理多個聊天機器人,選取『建立使用者』,並且輸入新的『提供者名稱』,然後按下同意,設定隱私權政策服務條款 可以跳過,最後確認資訊並點下確定啟用。

接下來再回來看剛剛那個頁面,會發現需要填入 Channel資訊Webhook網址

我們先進入 開發人員管理介面,前往 提供者列表(Provider List),選擇剛剛建立的提供者。

之後點擊 Create New Channel 進入,在選擇 Messaging API,另外兩個暫時不會用到。

選擇 Messaging API

Channel Access Token 頻道存取令牌

還記得剛剛的 Channel資訊 都是空的嗎?在我們建立完新的 Messaging API 後,找到 Messaging API 的標頭點下去,往下滑動找到 Channel access token (long-lived) 然後點擊 Issue 按鈕產生一組新的 Channel access token(頻道存取令牌),接著會跳出一個彈窗,選擇 0 hours,目的在於不讓這個頻道存取令牌過期。

點擊標頭切換頁面

然後把這行複製更改掉原本空的 Channel資訊Channel secret,在找到剛剛的頁面,複製標頭 Basic settings 裡的 Channel ID 貼到剛剛 Channel資訊Channel ID

到此LINE Bot建置基本上到一個段落,接下來我們會直接透過 Heroku 架設一個伺服器。

*如果想要使用本機測試,可以嘗試使用 Ngrok,詳細可以參考 ngrok 不求人:自己搭一個窮人版的 ngrok 服務 搭建一個 本地 server,之後再把上方提到原本空的 Webhook網址 填入自己的 Ngrok 網址。之後如果有更新會再補上教學。*


Heroku 雲平台

LINE Bot Webhook 伺服器 建置

透過 Heroku(註3) 的免費方案可以執行一個伺服器,但是伺服器 30分鐘 內沒有任何連線,程式會進入休眠,不過再次連線只是需要一點等待時間即可。

Heroku 帳號申請

首先進入 Heroku 網站 申請帳號,輸入紅色米字號的必要輸入資訊後送出,然後到信箱點擊驗證信件的網址驗證,最後到彈出的網頁輸入密碼,完成申請。

Heroku Cli 安裝

接著我們必須安裝 Heroku Cli,前往 Heroku 網站 Cli 下載點,依照你的作業系統下載安裝。

Git 版本控制軟體

這邊就不多做 Git 安裝介紹,有興趣可以參考 進度條

Git

註3: Heroku是一個支援多種程式語言的雲平台即服務。簡單說他就是一個伺服器,別人可以透過他給你的網址,執行你的程式;這個雲平台的優點是可以免費使用,所以你並不需要去自行架設或是擔心剛入門就要準備大量銀彈去繳學費。


LINE Bot 與日幣爬蟲應用

在建立一個新專案之前,這邊必須要來套用上次的爬蟲應用,但是要怎麼跟 LINE 結合呢?這邊就需要用到 LIEN 官方的 Node.js SDK,官方其實提供了好幾種的程式語言版本,但我幾經思考後還是選擇了 Node.js 畢竟 JavaScript 還是能多少無痛上手嘛(笑…

那怎麼引入套件可以參考之前的文章 [TOOL Notes]-關於Webpack,這邊就不再次說明,直接上Code

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
'use strict';
const line = require('@line/bot-sdk'); // line 的 sdk
const express = require('express'); // node.js 套件

var crawler = require('./crawler'); // 爬蟲的部分

// LINE 的 ENV 設定
const config = {
channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
channelSecret: process.env.CHANNEL_SECRET,
};
const client = new line.Client(config);
const app = express();
app.post('/callback', line.middleware(config), (req, res) => {
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result))
.catch((err) => {
console.error(err);
res.status(500).end();
});
});

// 測試回應用
app.get('/', (req, res) => {
res.json("ok");
});

function handleEvent(event) {
// 判斷是否為 Webhook URL 測試
if (event.replyToken === "00000000000000000000000000000000" || event.replyToken === "ffffffffffffffffffffffffffffffff") {
return Promise.resolve(null);
}
// 設定關鍵字 查詢匯率
else if (event.type === 'message' && event.message.text === '告訴我日幣匯率') {
// 跑日幣的爬蟲程式碼
crawler.crawler('JPY').then((res) => {
const msgToUser = {
type: 'text',
text: `日幣現金匯率為->${res.cashSellExchange}, \n日幣即期匯率為->${res.sightSellExchange}, \n詳細資訊請參考->${res.webSideUrl}`,
}
return client.replyMessage(event.replyToken, msgToUser);
});
}
return Promise.resolve(null);
}

const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`listening on ${port}`);
});

這邊我們很快速的講解過去,首先是 use strict 嚴謹模式(有興趣可以參考我的 JavaScript 系列),接下來引入 LINE SDKNode.js express 框架,隨後引入 crawler 這隻檔案,讓 Code 可以比較乾淨,等一下會講解另一隻檔案如何讓現在的檔案吃到。

接著 config 就是設定 LINE 的環境變數,這邊等等也會做講解,在程式碼完成之後,要傳上 Heroku 之前,會把環境變數設定上去。

最後在下面的 handleEvent 主要是判斷 event 的事件是,
接收到訊息該如何回應,裡面可以新增各式各樣的判斷,比如對方傳文字來或是圖片來,你應該要如何回應。在剛開始 LINE 那邊會有判斷是否為 Webhook URL 測試。在 Line Server 那邊會傳來 replyToken === “00000000000000000000000000000000”replyToken === “ffffffffffffffffffffffffffffffff” 若收到這樣的訊息可以不做回應。

接著就是 event.type === ‘message’ 而且 event.message.text === ‘告訴我日幣匯率’;意思就是如果收到訊息且內容是『告訴我日幣匯率』,那就去跑另一個 JavaScript 檔案 crawler 的 **Function -> crawler()**。

不論結果如何,記得要 return Promise.resolve(null);。

接下來會簡單講解一下另外一個頁面的程式碼

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
const request = require('request'); // 抓整個 html
const cheerio = require("cheerio"); // 用來抓 element 的 類似 JQuery 的選擇器
const fs = require("fs");

// 抓取日幣的爬蟲
function crawler(type) {
console.log('type', type);
return new Promise((resolve, reject) => {
request({
url: "http://rate.bot.com.tw/Pages/Static/UIP003.zh-TW.htm",
method: "GET"
}, function(error, response, body) {
if (error || !body) {
return 'nullnull';
} else {
const $ = cheerio.load(body); // 載入 body
const tr = $("tbody tr"); // 抓取每一行的 tr
const result = []; // 建立一個儲存結果的容器
const bankDate = $("#h1_small_id"); // 放台銀牌告匯率日期

let JPY_cashSellExchange = null; // 用來放日幣現金匯率的
let JPY_sightSellExchange = null; // 用來放日幣即期匯率的
let AUD_cashSellExchange = null; // 用來放澳幣現金匯率的
let AUD_sightSellExchange = null; // 用來放澳幣即期匯率的
let webSideUrl = 'http://rate.bot.com.tw/Pages/Static/UIP003.zh-TW.htm';
let resDate = null; // 用來放要回傳回去的匯率資訊

for (var i = 0; i < tr.length; i++) {
const coinType = tr.eq(i).find('.hidden-phone.print_show').text().trim(); // 幣別
const cashBuyExchange = tr.eq(i).children().eq(1).text(); // 現金匯率(銀行買入)
const cashSellExchange = tr.eq(i).children().eq(2).text(); // 現金匯率(銀行賣出)
const sightBuyExchange = tr.eq(i).children().eq(3).text(); // 即期匯率(銀行買入)
const sightSellExchange = tr.eq(i).children().eq(4).text(); // 即期匯率(銀行賣出)
const bankDateText = bankDate.text(); // 當日日期

// 抓日圓
if (type === 'JPY' && coinType === '日圓 (JPY)') {
// 放進 JSON 檔
result.push(Object.assign({ bankDateText, coinType, cashBuyExchange, cashSellExchange, sightBuyExchange, sightSellExchange }));

// 更新日幣變數
JPY_cashSellExchange = cashSellExchange;
JPY_sightSellExchange = sightSellExchange;

console.log('跑爬蟲的 Function 裡面', '現金匯率->', JPY_cashSellExchange, '即期匯率->', JPY_sightSellExchange)

resDate = {
cashSellExchange: JPY_cashSellExchange, // 現金匯率
sightSellExchange: JPY_sightSellExchange, // 即期匯率
webSideUrl: webSideUrl, // 爬蟲網站
}

}

// 抓澳幣
if (type === 'AUD' && coinType === '澳幣 (AUD)') {
// 放進 JSON 檔
result.push(Object.assign({ bankDateText, coinType, cashBuyExchange, cashSellExchange, sightBuyExchange, sightSellExchange }));

// 更新日幣變數
AUD_cashSellExchange = cashSellExchange;
AUD_sightSellExchange = sightSellExchange;

console.log('跑爬蟲的 Function 裡面', '現金匯率->', AUD_cashSellExchange, '即期匯率->', AUD_sightSellExchange)

resDate = {
cashSellExchange: AUD_cashSellExchange, // 現金匯率
sightSellExchange: AUD_sightSellExchange, // 即期匯率
webSideUrl: webSideUrl, // 爬蟲網站
}

}

}
// 回傳資訊
resolve(resDate);
}
});
});
};
// 輸出這個 JS
exports.crawler = crawler;

基本上與之前的程式碼大同小異,差別在於這次是把程式碼包進 new Promise,最後再靠 exports.crawler = crawler; 就能在其他檔案使用了。

一枝穿雲箭

最後這邊我們就可以把程式上傳了,在專案的目錄下,先輸入:

1
$ heroku create 名稱(如不填 系統會隨機給一個名稱)

其中要小心一些 heroku 的規定,比如開頭不能大寫之類的。

有錯誤

看到下方這張圖才代表成功。

成功創建

免費的方案似乎有專案的數量上限限制,目前創建到第五個以上就會被拒絕,如理解有誤敬請糾正,非常感謝。

隨後執行下列幾行,然後等待:

1
2
3
4
5
6
7
8
9
$ git init      // git init 初始化專案

$ heroku git:remote -a <Heroku 專案 名稱>

$ touch .gitingnore // 這邊 把不要上傳的 檔案 資料夾 都寫入這裡面

$ git commit -am "init my chatBox"

$ git push heroku master

整個操作起來其實跟推上 GitHub 非常像;傳送上去之後如果要看到作品可以直接輸入 https://dashboard.heroku.com/apps 進入後點擊創建的那個專案。

Heroku 首頁

接著到右上角點擊 Open app,就可以看到了,

Heroku Open APP

如果想看見程式的 log ,那就點擊右上角的 View logs,就可以查看你的程式碼有沒有錯誤了。

錯誤千軍萬馬來相見

這時候你可能會發現錯誤,其實是因為前面我們有提到,程式碼剛開始有 config 但是我們沒有只接填寫,變成他會去抓 Heroku 的設定,如果沒設定就會出錯。

輸入下面的程式碼(看情況自己增加):

1
2
3
$ heroku config:set CHANNEL_ACCESS_TOKEN="<Your LINE Bot Token>"

$ heroku config:set CHANNEL_SECRET="<Your LINE Bot Srcret>"

最後把 Open APP 打開後的網址貼到最上面我們提到的 Webhook網址,大功告成啦!


Conclusion & 結論

其實自己摸了好一陣子的 LINE 但是一直都沒有去瞭解過他怎麼執行還有流程是怎麼樣,這次剛好在玩爬蟲,就順水推舟連 Node.js 都一起研究研究,所以如果有講不好的地方還敬請歡迎指教。

LINE Bot 其實還有很多好玩的地方,這就等著大家自己去發掘啦~