🐍

[python,AWS]RSSをサーバーレスに自動取得してDiscordに送ってみる

2021/03/09に公開

はじめに

  • 生まれながらにサーバーレスなRSSリーダーを自作したい欲求があったので、自作しました
  • 以下を試しました
    • Lambdaでスクレイピング、Discordにwebhookで投稿
    • VSCode Remote Containerで開発
    • EventbridgeによるLambdaの定期実行
    • Lambdaをコンテナで動かす
    • Systems ManagerパラメータストアとLambdaの連携

成果物

  • 登録しておいたサイトのRSSが1時間おきに自動取得され、リンクとタイトルがDiscordに投稿されます
    Joinおめでとうございます

構成図

  • LambdaがRSS情報を取得し、webhookを利用してDiscordに通知します
    • LambdaはEventbridgeにより1時間おきに自動実行されます
  • RSS取得先URLはSystems Managerパラメータストアに保存します
    • URLの追加変更はデプロイを介さず可能です
  • Lambdaはコンテナイメージを利用して動きます、言語はpythonです
    • デプロイはGithub Actionsを利用して自動化しています

実装

  • リポジトリ全体はこちら

https://github.com/mini-hiori/lambda-rss-reader-bot

RSSリーダー本体(Lambda)

  • RSSの取得&解析はfeedparserというライブラリを利用して行います(参考)
    • 最小構成は以下です、get_rssにRSSのURLを渡すと記事情報が得られます
import feedparser
import time
from dataclasses import dataclass
from typing import List


@dataclass
class RssContent():
    title: str
    url: str


def get_rss(endpoint: str) -> List[RssContent]:
    feed = feedparser.parse(endpoint)
    rss_list: List[RssContent] = []
    for entry in feed.entries:
        if not entry.get("link"):
            continue
        rss_content = RssContent(
            title=entry.title,
            url=entry.link
        )
        rss_list.append(rss_content)
    return rss_list
  • Lambdaはコンテナイメージで動かします
    • Dockerfileはこちら
    • VSCode Remote Containerを利用すると、このDockerfileで作られるコンテナの中で開発ができます(参考)
    • コンテナを利用するLambdaの作成自体はGUIから可能です。
      作成時にコンテナイメージを保存したURIを求められるので、次項のデプロイを済ませてから作成します

デプロイ(Github Actions)

  • 参考
  • 上記記事を参考に以下を行うことでデプロイの自動化が可能です
    • ECRの作成
    • IAMユーザーの作成
    • Github Actionsのyamlファイル作成、.github/workflows/main.ymlに配置
    • GitHubのSecretsにIAMログイン情報等を記載
      • AWS_ECR_REPO_NAMEにはリポジトリ名を記載することに注意。URIではありません
        (1敗)

webhookURL・RSS取得先URLの管理(Systems Manager)

  • URL類はSystems Managerパラメータストアに保存します
    • RSS取得先URLについては改行区切りで単一パラメータに保存してしまっています
  • LambdaからSystems Managerを参照し、パラメータを改行で分割することでRSS取得先URLをLambdaに与えます
from typing import List
import boto3

ssm = boto3.client('ssm')

def get_target_url() -> List[str]:
    """
    取得対象にするrssのURLを返却する
    """
    url_param: str = ssm.get_parameter(
        Name='RSSURLList'
    )['Parameter']['Value']
    # url_paramには改行区切りでURLが保存されているので、改行で分割して出力
    url_list: List[str] = url_param.split("\n")
    return url_list
  • DiscordのwebhookURLについても同様にSystems Managerに保存しています

    • DiscordのwebhookURL自体の発行についてはこちら
  • ここまでで、Lambdaを手動実行すればRSSがDiscordに送られる状態が実現します

定期実行(Eventbridge)

  • EventbridgeをLambdaのトリガーに設定すると定期実行が可能です(参考)

  • 今回は1時間おきに実行するので「rate(1 hour)」で設定しました

    • rateは開始時間を設定できないようです。
      試した限りではEventbridgeの設定完了直後〜数分後までに1回目が起動して、以降はrateの周期にしたがって動きます
  • RSS情報が1時間おきにDiscordに送られれば完成です

改善点

  • LambdaやECR、Eventbridgeは今回手動作成しましたが、SAMやterraform,serverless frameworkでデプロイした方が再利用性があって良さそうです
  • pythonのDockerfileはalpineでない方がパフォーマンスが良いらしく、改善の余地があります(参考)
    • AWS公式もalpineで紹介していたので今回は流用しましたが、次回はbuster系で試す予定です
  • スクレイピング系の処理をLambdaで実行するのはコスパがあまりよくないそうです(参考)

Discussion