forestec

勉強した内容をつらつらと備忘録として記していきます。

Pythonで登録したRSSの新着記事をSlackで受け取るツールを作成してみた

定期購読したいサイトのRSS登録を行い、
新着記事が存在した場合はそれをSlackへ通知するツールをPythonで作成してみました。

機能

  • RSS登録機能
  • 登録RSS一覧表示機能
  • RSS削除機能
  • 新着記事通知機能

ソースはgithubにも上げているので詳しくはそちらを参照下さい。 github.com

RSS登録機能

Slack上からスラッシュコマンドで新規RSSを登録する機能
RSS情報はRedisのハッシュを使って以下のように管理

('RSSから取得したブログタイトル', 'url', 'スラッシュコマンドから受け取ったRSSURL')
('RSSから取得したブログタイトル', 'previous_time', '最後に取得した最新記事の作成日時')

ここに登録したprevious_timeより作成日時が新しい記事のみ取得してSlackへ送信しています。

登録コマンドで送られてくるRSSURLのチェック

# URL形式チェック
if re.match(r'^https?:\/\/', url):
    rss = feedparser.parse(url)
    …
else:
    msg = 'URLは http://、https:// から記述してください。'

http://https://で始まる文字列でない場合はエラーメッセージを返却

f:id:forest_yuzuremon:20181204013121p:plain

RSSを読み込んだ結果の判定も実施

rss = feedparser.parse(url)
# パースが成功している場合のみ処理を行う
if rss.bozo == 0:
    title = rss.feed.title
    self.redis.add(title, url, self.time_format(rss.entries[1].published))
    msg = 'RSS登録完了: {}'.format(title)
else:
    # パースに失敗した場合はエラーを出力して処理続行
    print('url: {}, error: {}'.format(url, rss.bozo_exception))
    msg = 'URLが存在しないか、またはRSSとして読み取り出来ませんでした。'

feedparserから返却されるbozoで判定
bozoが0ならパース成功、
bozoが1ならパース失敗

f:id:forest_yuzuremon:20181204013010p:plain

RSS一覧表示機能

Slack上からスラッシュコマンドで登録されているRSSの一覧表示を行う機能

# RSSの一覧を表示
msg = 'RSSList : \n - ' + '\n - '.join([key.decode() for key in self.redis.get_key_all()])

Redisからkeyの一覧を取得してmsgに詰めているだけです。
見た目が悪かったので1タイトルずつ改行して表示させています。

f:id:forest_yuzuremon:20181204012859p:plain

RSS削除機能

Slack上からスラッシュコマンドで登録されているRSSを削除する機能
一覧表示機能で登録されているRSSを確認して削除するイメージです。

# RSS削除を実施
title = params[b'text'][0].decode()
self.redis.delete(title)
msg = 'RSS削除完了: {}'.format(title)

スラッシュコマンドで受け取ったタイトルをkeyにしてRedisから削除しているだけです。

f:id:forest_yuzuremon:20181204012740p:plain

新着記事通知機能

登録されているRSSを順番に読み込んでいき、
新着記事が存在する場合のみSlackへ通知を行っています。

登録されているkey分繰り返し処理

# 登録されたRSS数分処理を行う
for key in self.redis.get_key_all():
    # RSSのURL、最後に取得した記事の作成日時を取得
    rss_config_dict = self.redis.get(key)
    url = rss_config_dict[b'url'].decode()
    # RSS読み取り
    rss = feedparser.parse(url)
    …

Redisから取り出したkeyに紐付くRSSURLを読み込んでパースしています。

Slackへ送信するattachmentsを作成

def create_attachments(self, rss, last_items, key):
    """取得した記事からattachmentsを生成"""
    attachments = []
    for i in range(last_items, -1, -1):
        # 取得した記事の作成日時を保存
        published = rss.entries[i].published
        self.redis.update_previous_time(key, self.time_format(published))

        summary = rss.entries[i].summary
        # 投稿した記事をattachmentに追加
        attachments.append(
            {
                'title' : '<{}|{}>'.format(rss.entries[i].link, rss.entries[i].title),  # タイトルと記事のリンク
                'text' : re.compile(r'<[^>]*?>').sub('', summary) if summary is not None else '',  # 記事のサマリー
                'footer' : self.time_format(published)  # 記事の投稿時間
            }
        )
    return attachments

last_itemsには取得する新着記事が1件の場合は0、2件の場合は1が入ってくるイメージです。
Redisのprevious_timeを記事の作成日時で更新します。
これで次回実行時に同じ記事を取得しないようにしています。
Slackへ送信しているのはtitlelinksummarypublishedのみです。

それをスレッド化して送信すると以下のようになります。

f:id:forest_yuzuremon:20181204014432p:plain

スレッドを開くと以下のようになっています。

f:id:forest_yuzuremon:20181204014542p:plain

こんな感じでSlackからブログの購読が可能になりました。
自分はこれをcronで毎朝7時に実行されるようにして通知を受け取っています。