PTT新貼文提醒、單字蒐集 Chrome 擴充套件?一同窺探有哪些強大的 API 可以使用
大家是否有在使用瀏覽器『擴充套件』呢?筆者已經使用 uBlock 這款開源的廣告阻擋器多年囉,但是一直以來都不知道瀏覽器擴充套件是如何製作以及其可取用的 API 為何,前陣子花了點時間研究一下,才發現在 Chrome/Chromium 上開發擴充套件竟然是如此的簡單,而且可取用的 API 跟在 Web 上也完全不是同一個檔次,今天就來看看這些東西,順便找些場景來應用。
如何建立,並開始開發 chrome 擴充套件
首先去看了官方文件:https://developer.chrome.com/extensions/getstarted
請先建立一個空的資料夾作為擴充套件專案資料夾,接著在資料夾中建立 manifest.json
,用來設定擴充套件的名字,敘述等:
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"manifest_version": 2
}
接著打開 Chrome 瀏覽器到 chrome://extensions/
(在網址列上直接輸入即可),右上角有個 Developer mode
(開發模式),把它打開:
你會發現有幾個按鈕跑出來:
按下 Load unpacked
並且選取 manifest.json
所在的資料夾,接著可以看到擴充套件就這樣出現了:
沒錯,就是這麼簡單,幾乎不用任何開發工具,就是 chrome 跟你最愛的編輯器即可;當然 manifest.json
還能放其他欄位,例如加上 icons
:
{
...
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
...
}
icon.png
(或是其他檔名也可以)隨便找一張圖即可,就直接放在跟 manifest.json
同個資料夾下即可;在修改之後會需要來按下這邊的重整按鈕 更新擴充套件,這樣一來我們就可以測試剛改好的擴充套件是否如預期執行:
Chrome 擴充套件能做哪些事?
上面建立了一個 chrome 擴充套件,但是它什麼功能也沒有,毫無反應;官方文件有一頁概觀列出擴充套件上可以使用的功能:
https://developer.chrome.com/extensions/devguide
這篇文章將以兩個情境來介紹筆者比較有興趣的幾個功能
情境 1: PTT 新文章提醒
其實就是文章爬蟲,只是透過 chrome 瀏覽器定期進行檢查,並且比對是否跟上次爬的資料一樣,不一樣的話通知使用者,這樣聽起來會需要幾個功能:
- 定期在背景執行程式
- 進行 http call 抓回網頁資料
- 資料儲存功能
- 通知功能
在官方 get started 建立擴充套件之後第一件事就是在 manifest.json
給一個 background.js
讓擴充套件有程式可以執行:
{
...
"background": {
"scripts": ["background.js"],
"persistent": false
},
...
}
可以先在 background.js
寫上一個 console.log('hello chrome extension!')
測試一下,並請他在安裝好的時候執行:
chrome.runtime.onInstalled.addListener(() => {
console.log('hello chrome extension!');
})
重整更新之後可以看到 Inspect views background page
出現了,點下去就是熟悉的 Chrome DevTools:
定期在背景執行程式
這個功能叫做 alrams,首先必須要在 manifest.json
加入權限:
{
...
"permissions": ["alarms"],
...
}
接著在安裝好擴充套件時加入 alarm,這邊請他每分鐘執行一次:
chrome.runtime.onInstalled.addListener(() => {
chrome.alarms.create('check', { periodInMinutes: 1 })
})
同時加上 alarm callback 作為定期執行的程式:
chrome.alarms.onAlarm.addListener(alarm => {
console.log("Got an alarm!", alarm);
});
一樣戳一下重整按鈕,一分鐘之後應該可以看到:
進行 http call 抓回網頁資料
既然是在瀏覽器的 Javascript 環境,第一個想到的 API 就是 fetch
了,假設要來抓 PTT mobilesales:
fetch('https://www.ptt.cc/bbs/mobilesales/index.html')
馬上就會看到錯誤發生,Chrome 也很好心地直接在擴充套件管理界面上讓你知道有事情不太對勁:
沒錯,跨站存取問題,不過我們可是擴充套件,只要在 manifest.json
的 permission
給上允許存取的 URL pattern 即可,像是這樣就可全開:
{
...
"permissions": [..., "*://*/"]
...
}
那麼接下來的問題就是如何從 html 中找到要找的資料,筆者使用 DOMParser,接著就可以用 Web/DOM API 享受找元素的方便性(例如 querySelector
),迅速找到 mobilesales 最新的文章標題。
資料儲存功能
Chrome 擴充套件提供了自己的 storage API,首先得上 "storage"
到 manifest.json
的 permission
:
{
...
"permissions": [..., "storage"]
...
}
接著就可以透過這樣的語法進行讀寫,把 fetch
到的文章主題存起來:
chrome.storage.local.set({ key: value }, callback)
chrome.storage.local.get('key', result => { console.log(result.key) })
這組 API 是非同步的,所以需要 callback 來接,筆者看到這就立刻用 Promise 包起來接下來就能用 async/await 寫更漂亮的語法。
通知功能
定期抓資料回來比對跟上次存起來的文章標題是否一樣,不一樣的時候就是有新文章,這時通知使用者,chrome 擴充套件也有 notifications API,第一件事情一樣是加上 manifest.json
的 permission
:
{
...
"permissions": [..., "notifications"]
...
}
我們就可以呼叫 API 通知使用者:
chrome.notifications.create('reminder', {
type: 'basic',
iconUrl: 'icon.png',
title: 'ptt mobilesales first post:',
message: '[賣/台北/面交] iPhone ...', // fetch 到的最新文章主題
});
接下來就是寫 Javascript 進行邏輯串接了,筆者也有進行一些改良,完整程式碼請見 Github repo: https://github.com/pastleo/ptt-watcher-chromext
情境 2: 單字蒐集
這個就有趣了,使用者在任何網頁上瀏覽時,看到一個沒學過的單字想要紀錄下來,於是把文字選取起來,按下隨即出現的 Add
按鈕把文字加入複習清單內。點選 UI 上的擴充套件 icon 會有 popup 看到蒐集的單字數量;也可以打開一個擴充套件頁面來進行複習,這樣聽起來會需要幾個功能:
- 在瀏覽中的網頁上執行指定的 Javascript
- 而且可以存取到頁面上的 BOM/DOM、擴充套件 API
- 點選擴充套件 icon 有個 popup
- 打開並顯示一個複習頁面
當然還是需要資料儲存功能,上面已經提過就不再提,這部份最有趣的部份就是 在瀏覽中的網頁上執行指定的 Javascript
,也就是在別人的網站上執行自己寫的 Javascript。
在瀏覽中的網頁上執行指定的 Javascript
這個功能就是 content_scripts
,需要 activeTab
permission,所以當然還是得先在 manifest.json
上寫好,同時也要告訴 chrome 你要執行的 Javascript 檔名跟執行的網頁 URL pattern:
{
...
"permissions": [..., "activeTab"],
"content_scripts": [{
"matches": ["https://*/*", "https://*/*"],
"js": ["contentScript.js"]
}]
...
}
照這樣設定,在瀏覽任何網頁時都會在上面執行 contentScript.js
,我們就可以所有網站加上我們自己想要的行為。
關於 contentScript.js
- 根據官方文件,
contentScript.js
預設會在document_idle
時執行,也就是說至少都已經DOMContentLoaded
contentScript.js
可以透過window
/document
操作網頁的 BOM/DOM- 有趣的是,
contentScript.js
同時還可以呼叫chrome.storage
等擴充套件才有的 API
根據官方文件,
contentScript.js
跟原本網頁是不同的 Javascript runtime 實體,經過測試確實是如此,使用window.testVar = 123
在另外一邊是拿不到的,但是 BOM/DOM 操作對象是同一個
文字選取偵測,偵測到時加上按鈕
策略很簡單,就是在整個 document
上監聽 click
事件,並使用 window.getSelection()
看是否有文字被選取,有的話就放上按鈕把選取的文字儲存起來:
document.addEventListener('click', event => {
const selected = window.getSelection().toString();
if (!selected) return;
button = document.createElement('button');
button.textContent = 'Save this';
// 設定 button.style 使得按鈕出現在滑鼠附近
button.addEventListener('click', () => {
// 透過 chrome.storage 把 selected 存起來
});
document.body.appendChild(button);
})
Popup 以及複習頁面
- 點選擴充套件 icon 時的行為可以透過設定 browserAction 來決定,指定
default_popup
到一個 html 檔案即可 - 複習頁面的部份就建立一個 html 網頁
- 同時我們來把這個複習頁面當成 擴充套件選項頁(Options)
在 manifest.json
中指定這些 html 檔案:
{
...
"browser_action": {
"default_popup": "popup.html"
},
"options_page": "review.html",
...
}
接下來內容排版、行為實做就是一般靜態網頁的開發,唯一的差別就是多了擴充套件 API 可以使用,像是上面提到的 storage API
擴充套件的所有資源都有一個特定的 URL 可以連到,透過 chrome.runtime.getURL('review.html')
得到擴充套件內 review.html
的 URL,然後在 popup html 內加入一個按鈕來開啟這個 URL。
想知道
chrome.runtime
還有什麼 API 可用?請參考官方文件
同樣地筆者也進行一些改良,簡易字卡蒐集小工具就這樣完成囉:
完整程式碼請見 Github repo: https://github.com/pastleo/word-cards-chromext
今天透過兩個場景介紹了幾個基礎的擴充套件 API,光是這些東西就已經足以做很多強大的功能了,而且還不只有這些,它甚至還可以操作書籤、Tabs 等,就算沒有要發布到 Chrome web store,用自己寫的程式解決日常使用 Chrome 瀏覽時遇到的問題也是很有趣的,接下來就讓各位讀者們自己去探索囉!
同時筆者會基於這篇『單字蒐集』的擴充套件進行加強,開設工作坊給大家一起學習體驗,有興趣的朋友請關注五倍粉絲團:https://www.facebook.com/5xruby
本文同步發表在筆者的部落格:https://pastleo.me/post/20200731-chromext-apis-word-cards