自分のサイトのはてなブックマーク、どれぐらいついたのか監視したくなりました。
ので作りました。
基本方針の確認
先人の知恵を借りましょう。
おおよそのロジックは うわっ…私のはてブ数、低すぎ…?GASを使って、はてブ数を監視(通知)する方法! | コンパス
という記事の実装がよかったので参考にしました。つまり…
- はてブのRSSを叩いてパースし、シートに書き入れる
- なんらかの手段で はてブ数をカウントする
- すでに記入済みのものとの差分をメッセージとしてスタックする
- 取得したカウント数でシートを更新する
- Slackで通知する
上述のような流れです。
ここで、前述の記事では はてブAPI を使っています。
が、HTTPS化に伴ってCORS許可がされてないようなのでエラーになってしまいます。
(公開APIとは……なにかの間違いであってほしいのですが)
そこで、 Qiitaの指標をGASで集計してNotion Chartsで可視化する - Qiita この記事を参考にはてブ数を取得します。
やってみた
ソースコード
Googleスプレッドシートを作り、hatenaというシートを作ります。
スクリプトエディタへソースコードをコピペし、
シートのURLとSlackの incoming webhookのURL、対象URLを差し替えてください。
hatenaChecker 関数を時間トリガーで実行するようにしてください。
私は1時間毎に実行しています。
// 書き込み先シート名
const sheetName = "hatena";
// 自分のシートのURLに合わせる
const sheet = SpreadsheetApp.openByUrl(
"https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxx/edit#gid=0"
).getSheetByName(sheetName);
// Slackのincoming WebhookのURLに書き換える
const incomingWebhookURL =
"https://hooks.slack.com/services/XXXXXXXX/XXXXXXX/xxxxxxxxxxxxxxxxxx";
// 配列で複数指定できます
const targetUrls = [
// "https://qiita.com/fruitriin",
// "https://note.com/fruitriin",
];
const header = "■はてブ増減チェック\n";
function hatenaChecker() {
addUrl();
const message = createMessage();
sendSlack(message);
}
function createMessage() {
const rows = sheet.getSheetValues(1, 1, sheet.getLastRow(), 3);
let message = "";
let rowIndex = 1;
for (let row of rows) {
const url = row[0];
const title = row[1];
const newCount = fetchBookmarkCount(url);
const oldCount = row[2];
// 変化がなければスキップ
if (oldCount == newCount) {
rowIndex++;
continue;
}
// 新規追加
if (oldCount == "") {
message += `新規:${newCount} |${slackLink(title, url)}\n`;
} else {
// 増減
if (newCount > oldCount) {
message += `+${newCount - oldCount} |${slackLink(title, url)}\n`;
} else {
message += `-${oldCount - newCount} |${slackLink(title, url)}\n`;
}
}
sheet.getRange(rowIndex, 3).setValue(newCount);
rowIndex++;
}
return message;
}
function slackLink(text, url) {
return `<${url}|${text}>`;
}
function addUrl(url) {
//urlを自動で追加
const atom = XmlService.getNamespace("http://purl.org/rss/1.0/");
let items = [];
for (let url of targetUrls) {
const feed_url =
"http://b.hatena.ne.jp/entrylist?url=" + url + "&mode=rss&sort=eid";
//たまにエラーになるらしい
try {
var document = XmlService.parse(
UrlFetchApp.fetch(feed_url).getContentText()
);
} catch (e) {
//新規取得URL取得は諦めて続行
return;
}
items = [...items, ...document.getRootElement().getChildren("item", atom)];
}
const hatebus = [];
for (let item of items) {
hatebus.push({
url: item.getChildText("link", atom),
title: item.getChildText("title", atom),
}); //urlを取得
}
const last_row = sheet.getLastRow();
const sheet_url = [];
// すでにシートに書き込まれているURLを配列に詰める
for (let i = 1; i <= last_row; i++) {
sheet_url.push(sheet.getRange(i, 1).getValue());
}
// 新規のURLについてはシートに書き込む
for (let hatebu of hatebus) {
if (sheet_url.indexOf(hatebu.url) == -1) {
sheet.getRange(sheet.getLastRow() + 1, 1).setValue(hatebu.url);
sheet.getRange(sheet.getLastRow(), 2).setValue(hatebu.title);
}
}
}
// ブックマークカウントの集計
function fetchBookmarkCount(url) {
const redirectUrl = getRedirectUrl(`http://b.hatena.ne.jp/bc/${url}`);
// `https://b.st-hatena.com/images/counter/default/00/00/0000653.gif` の形式で
// ブクマ数が書かれたgif画像のURLを取得できるので、そこからブクマ数だけを抽出する
const bookmarkCount = redirectUrl.match(
/https:\/\/b.st-hatena\.com\/images\/counter\/default\/\d+\/\d+\/(\d+).gif/
)[1];
return Number(bookmarkCount);
}
// リダイレクトURLの取得
function getRedirectUrl(url) {
try {
const response = UrlFetchApp.fetch(url, {
followRedirects: false,
muteHttpExceptions: false,
});
const redirectUrl = response.getHeaders()["Location"];
if (redirectUrl) {
const nextRedirectUrl = this.getRedirectUrl(redirectUrl);
return nextRedirectUrl;
} else {
return url;
}
}catch (e ){
return;
}
}
function sendSlack(message) {
if (message == "") return;
var payload = {
text: header + message,
};
const params = {
contentType: "application/json",
method: "POST",
payload: JSON.stringify(payload),
};
var response = UrlFetchApp.fetch(incomingWebhookURL, params);
}
雑感
まとめて範囲選択して二重配列で読み書きを行うとパフォーマンスが段違いに上がるのですが
動いたところで満足してしまったので修正してません
なんかたまに例外を吐いて死にます。try catchが全部できてないらしい。
JSで書いてますがTSのほうがGAS書くの簡単だと思います。