Chrome拡張のRSSの検出方法とロジック
今回紹介する内容
自作しているRSSリーダに搭載している機能で閲覧したページでのRSSをどのように検出しているかをご紹介します。
Chrome拡張について
自作しているRSSリーダはChome拡張で作っています。Chrome拡張はHTML, CSS, JavaScriptを使ってChromeに機能を追加する仕組み[1]です。
作成するにはGoogleさんが公開している仕様に従って作る必要があるのですが、他の方々がが分かりやすく記事を書いてくださっているので、この記事では省略いたします。
自分で試したいだけであれば「拡張機能」タブの「パッケージ化されていない拡張機能を読み込む」で自分のChromeを拡張できます。
ご興味がある方は是非お試ししてみてはいかがでしょうか。
あらすじ
処理のあらすじです。
- コンテンツスクリプトでRSSを検索
- サービスワーカーへRSS候補を送る
- サービスワーカーでRSS候補を受け取る
- RSSを検出したタブにバッチをつける
これでローカルストレージへRSSの候補が保存されます。
1.コンテンツスクリプトでRSSを検索
コンテンツスクリプトとは
検出にはコンテンツスクリプトという機能を利用しています。この機能は指定したパターンに一致するURLのページを開いたときにDOMにアクセスできるプロセスでJavaScriptを実行することができます。この機能を利用するにはマニフェストで設定する必要があります。
コンテンツスクリプトを利用するためのマニフェストの設定方法
今回の処理では全てのURLに対して動作[2]
JavaScriptではjQueryを事前に読み込ませて自分の処理で利用しています。
... 略 ...
"content_scripts": [
{
"matches": ["<all_urls>"], // 全てのURLを指定
"js": [
"third-party/jquery.min.js", // 同梱しているjQueryを利用
"content-script.js" // コンテンツスクリプト本体
]
}
],
... 略 ...
コンテンツスクリプト本体
コンテンツスクリプトでは実行直後にjQueryを利用して
- XMLビュワーか判断 👉 XMLビュワーならそのまま中まで判断
- そうでない場合は、
a
タグを検索しRSSとなりそうなhref
を検索
XMLビュワーか判断
XMLビュワーかの判断は以下
$(function () {
/// スタイルがxml-viewer-styleか
const headCheck = $('html head style[id="xml-viewer-style"]')
if (headCheck.length > 0) {
// xmlと判断する。
// id='webkit-xml-viewer-source-xml' がxml本体
const xml = $('html body div:first[id="webkit-xml-viewer-source-xml"]')
const rss = analyze(xml) // xmlがRSSフォーマットか確認
XMLがRSSかの判断
RSSかどうかの判断はXMLの最初のタグ名で判断
function analyze (xml) {
const root = xml.find(':first')[0]
const tagName = root.tagName
switch (tagName) {
case 'rdf:RDF':
return {
rss: 'RSS1.0',
}
case 'rss':
return {
rss: 'RSS2.0',
}
case 'feed':
return {
rss: 'ATOM',
}
}
return { rss: undefined }
}
a
タグを探す
XMLビュワーでなければ、Webページでは開いた後にJavaScriptを使ってDOMの操作をする恐れがあります。
それを考慮してページを開いてから3秒待ち、その後a
タグを検索します。
a
タグを見つけたら
href
属性を確認し指定されているURLが以下の場合にRSSの候補としています。
- クエリ文字を排除したURLの終わりが次のいずれかであればRSSの候補とする。
-
.rss
,.rdf
,.atom
,.feed
,.xml
,/rss
,/rdf
,/atom
,/feed
,/xml
setTimeout(() => { // 3秒待つ
// aタグのリンク先が .rss .rdf .atom .feed .xml /rss /rdf /atom /feed /xml だった場合に候補とする
// 上記の候補で終わるURLであるか直後にクエリ文字列ある場合に対応
const regex = /(\.|\/)(rss|rdf|atom|feed|xml)([?].+)?$/i
const urls = Array.from(new Set($(`
a[href*=".rss"],
a[href*=".rdf"],
a[href*=".atom"],
a[href*=".feed"],
a[href*=".xml"],
a[href*="/rss"],
a[href*="/rdf"],
a[href*="/atom"],
a[href*="/feed"],
a[href*="/xml"]`)
.map((_, i) => $(i).prop('href'))
.filter((_, i) => i.match(regex))
))
... 略 ...
}, 3000) // 3000=3秒
2.サービスワーカーへRSS候補を送る
RSSの候補を見つけたらバックグラウンドのサービスワーカーへメッセージとしてRSSのURLリストを送ります。
chrome.runtime.sendMessage(
chrome.runtime.id,
{
type: 'detected-rss',
data: {
urls // 検出したURLリスト
}
}
)
3.サービスワーカーでRSS候補を受け取る
そもそもサービスワーカーって?
バックグラウンドで動作するJavaScriptです。DOMがない環境で動作します。
コンテンツスクリプト等はタブごとに複数立ち上がり動作しますが、これは唯一のプロセスとして動作します。コンテンツスクリプトとの通信はメッセージを送りあってやりとりします。こちらもマニフェストで設定することで利用できます。
サービスワーカーを利用するためのマニフェストの設定
サービスワーカーは以下のマニフェストで動作定義します。
なおサービスワーカーのJavaScriptファイルはマニフェストファイルと同じフォルダに含める必要があるそうです。
... 略 ...
"background": {
"service_worker": "background.js"
}
... 略 ...
バックグラウンドのロジック
メッセージのリスナを登録します。メッセージは汎用性がありRSSの検出以外の用途にも使います。
ですのでこのアプリでは必ずtype
にそのメッセージがなんなのか、data
にそのデータを入れるようにしています。
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'detected-rss') { // RSSの候補リスト
const rssUrls = message.data.urls
// ...略...
// urls を 検出時間を追加してローカルストレージへ保存
// ...略...
sendResponse?.() // コールバックがあれば呼び出しておく
return true
}
return false
})
4.RSSを検出したタブにバッチをつける
コンテンツスクリプト本体では自身にバッチをつけることができません。サービスワーカーにつけてもらいます。
さっきのbackground.js
のソースの続きでバッチをつけるロジックを追加します。
// ...略...
// 999件を超える場合は '999+' と表示、それ以下は数値そのまま
chrome.action.setBadgeText({
text: `${message.data.urls.length > 999 ? '999+' : message.data.urls.length}`,
tabId: sender.tab.id
})
// ...略...
最後に
RSSの検出方法とロジックのご紹介でした。他にも時間があればChrome拡張の記事を書いていこうと思います。
Discussion