住宅情報など、日々更新される特定の情報をキャッチアップすることは重要ですが、情報ソースを巡回するのは非効率的です。
そのため、毎日必ず確認するツール(今回はGmail)へ、Google Apps Script (GAS) を利用してRSSフィードを自動配信するプログラムを作成しました。
以下の要件です。
- 目的 住宅情報サイト(今回は、https://www.housenews.jp/ )の更新を検知
- 手段 Gmailにて情報の一元管理
- コスト 無料(GASの実行環境を利用)
- 運用 定期的な自動配信
◎ システムの設計と技術的要素
本システムの機能は、
「RSSフィードの取得・解析」
「新着記事の判定」
「HTMLメールの生成・送信」
から構成されます。
処理フローの概要
- 定期実行
- GASのタイムドリブントリガー(例 毎日午前9時)により 主関数が起動されます。
- フィード取得
UrlFetchApp.fetchにより指定されたRSSフィード(XML)を取得します。
- 新着判定
XmlService.parseでXMLを解析し、記事一覧(<item>要素)を取得します。- Script Properties に保存されている前回送信済みリンク一覧と照合し、重複していない記事のみを新着記事として抽出します。
- メール配信
- 新着記事がある場合、記事情報から体裁を整えたHTMLメール本文を生成し、メールの配信します。
- 新着記事がない場合も、動作確認のため「新着なし」の旨を通知するメールを送信します。
- 履歴更新
- 配信後、最新のRSS記事リンクをScript Propertiesに保存し、次回の実行に備えます。
rss-delivery-system.gs
// RSS to Gmail 配信システム
// Google Apps Scriptで実装
const RSS_FEED_URL = ‘https://www.housenews.jp/feed’; // 👈 取得したいRSS URL
const RECIPIENT_EMAILS = [
Session.getActiveUser().getEmail(), // 自分のメールアドレス
‘komatsu.ng@om.asahi-kasei.co.jp’ // 👈 追加の送信先
];
const SCRIPT_PROPERTIES = PropertiesService.getScriptProperties();
/**
* メイン関数:RSSフィードを取得してメールで送信
*/
function sendRSSFeedByEmail() {
try {
const xml = UrlFetchApp.fetch(RSS_FEED_URL).getContentText();
const document = XmlService.parse(xml);
const root = document.getRootElement();
// RSS 2.0形式を想定
const channel = root.getChild(‘channel’);
const items = channel.getChildren(‘item’);
// 前回送信した記事のリンクを取得
const lastSentLinks = getLastSentLinks();
const newItems = [];
// 新しい記事のみを抽出
for (let i = 0; i Math.min(items.length, 10); i++) {
const item = items[i];
const link = item.getChildText(‘link’);
if (!lastSentLinks.includes(link)) {
newItems.push({
title: item.getChildText(‘title’),
link: link,
description: item.getChildText(‘description’) || ”,
pubDate: item.getChildText(‘pubDate’) || ”
});
}
}
// 新着記事の有無に関わらずメール送信
if (newItems.length > 0) {
const emailBody = createEmailBody(newItems, channel.getChildText(‘title’));
MailApp.sendEmail({
to: RECIPIENT_EMAILS.join(‘,’),
subject: `【RSS更新】${channel.getChildText(‘title’)} – ${newItems.length}件の新着記事`,
htmlBody: emailBody
});
// 送信済みリンクを保存
saveLastSentLinks(items.slice(0, 10).map(item => item.getChildText(‘link’)));
Logger.log(`${newItems.length}件の新着記事を${RECIPIENT_EMAILS.length}名に送信しました`);
} else {
// 新着記事がない場合のメール送信
const noNewsBody = createNoNewsEmailBody(channel.getChildText(‘title’));
MailApp.sendEmail({
to: RECIPIENT_EMAILS.join(‘,’),
subject: `【RSS更新】${channel.getChildText(‘title’)} – 新着記事はありません`,
htmlBody: noNewsBody
});
Logger.log(‘新着記事なしのメールを送信しました’);
}
} catch (e) {
Logger.log(‘エラーが発生しました: ‘ + e.toString());
// エラー通知メール(オプション)
MailApp.sendEmail({
to: RECIPIENT_EMAILS.join(‘,’),
subject: ‘【エラー】RSS配信システム’,
body: ‘RSSフィードの取得中にエラーが発生しました:\n\n’ + e.toString()
});
}
}
/**
* HTMLメール本文を作成
*/
function createEmailBody(items, feedTitle) {
let html = `
<html>
<head>
<style>
body { font-family: ‘Helvetica Neue’, Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #4285f4; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
.item { background-color: #f9f9f9; margin: 15px 0; padding: 15px; border-left: 4px solid #4285f4; border-radius: 3px; }
.item-title { font-size: 18px; font-weight: bold; margin-bottom: 8px; }
.item-title a { color: #1a73e8; text-decoration: none; }
.item-title a:hover { text-decoration: underline; }
.item-date { color: #666; font-size: 12px; margin-bottom: 10px; }
.item-description { color: #555; font-size: 14px; }
.footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; color: #999; font-size: 12px; text-align: center; }
</style>
</head>
<body>
<div class=”container”>
<div class=”header”>
<h2 style=”margin: 0;”>${feedTitle}</h2>
<p style=”margin: 5px 0 0 0;”>${items.length}件の新着記事</p>
</div>
`;
items.forEach(item => {
// HTMLタグを除去(簡易版)
const description = item.description.replace(/<[^>]*>/g, ”).substring(0, 200);
html += `
<div class=”item”>
<div class=”item-title”>
<a href=”${item.link}” target=”_blank”>${item.title}</a>
</div>
<div class=”item-date”>${item.pubDate}</div>
<div class=”item-description”>${description}${description.length >= 200 ? ‘…’ : ”}</div>
</div>
`;
});
html += `
<div class=”footer”>
このメールは自動配信されています<br>
配信元: ${RSS_FEED_URL}
</div>
</div>
</body>
</html>
`;
return html;
}
/**
* 新着記事がない場合のメール本文を作成
*/
function createNoNewsEmailBody(feedTitle) {
return `
<html>
<head>
<style>
body { font-family: ‘Helvetica Neue’, Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #9e9e9e; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
.content { background-color: #f9f9f9; margin: 15px 0; padding: 30px; text-align: center; border-radius: 3px; }
.content p { font-size: 16px; color: #666; margin: 10px 0; }
.footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; color: #999; font-size: 12px; text-align: center; }
</style>
</head>
<body>
<div class=”container”>
<div class=”header”>
<h2 style=”margin: 0;”>${feedTitle}</h2>
<p style=”margin: 5px 0 0 0;”>配信状況レポート</p>
</div>
<div class=”content”>
<p>📭 新着記事はありません</p>
<p style=”font-size: 14px; color: #999;”>前回のチェック以降、新しい記事の更新はありませんでした。</p>
</div>
<div class=”footer”>
このメールは自動配信されています<br>
配信元: ${RSS_FEED_URL}
</div>
</div>
</body>
</html>
`;
}
/**
* 前回送信した記事のリンクを取得
*/
function getLastSentLinks() {
const data = SCRIPT_PROPERTIES.getProperty(‘lastSentLinks’);
return data ? JSON.parse(data) : [];
}
/**
* 送信済みリンクを保存
*/
function saveLastSentLinks(links) {
SCRIPT_PROPERTIES.setProperty(‘lastSentLinks’, JSON.stringify(links));
}
/**
* トリガーを設定する関数(初回のみ実行)
*/
function setupDailyTrigger() {
// 既存のトリガーを削除
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(trigger => ScriptApp.deleteTrigger(trigger));
// 毎日午前9時に実行するトリガーを設定
ScriptApp.newTrigger(‘sendRSSFeedByEmail’)
.timeBased()
.atHour(9)
.everyDays(1)
.create();
Logger.log(‘毎日午前9時の配信トリガーを設定しました’);
}
/**
* 1時間ごとに実行するトリガーを設定
*/
function setupHourlyTrigger() {
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(trigger => ScriptApp.deleteTrigger(trigger));
ScriptApp.newTrigger(‘sendRSSFeedByEmail’)
.timeBased()
.everyHours(1)
.create();
Logger.log(‘1時間ごとの配信トリガーを設定しました’);
}
/**
* テスト実行用(手動実行して動作確認)
*/
function testRun() {
sendRSSFeedByEmail();
}