6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

HaskellでRSSを生成してみる

Last updated at Posted at 2020-07-31

今さらRSS?

Webサイトの記事更新を効率よく知らせたり取得できたりするRSS。いくつかのRSSリーダーサービスが終了したりと、RSSについての盛り上がりは過去のもの、という話もありますが、「たいした仕込み要らずで、簡易通知機能を実装する」という観点でいうと、これはこれでありかもしれません。

というわけで、HaskellでRSS生成やってみたので、QuickStart的に書いてみました。

RSSとは

ある意味マイナーになってきつつあるRSSなので、「RSSとはなんぞや」をごくごく簡単に書いてみます。

RSS自体は「更新記事のアイテムをXML形式で記述したテキストファイル」です。場合によってはCSSもセットにすることもあります。RSSを静的に生成しておくと、Webサーバにおいておくだけで、通知のブロードキャストを軽量に実現できます。もちろんユニキャストとして使うのも自由。動的にRSSを生成したりするのも、どうぞご自由に。本記事とは直接関係はないですが、RSSクライアントは、SNSのbotと違い、pull型でデータを取得することになります。

RSSにはいくつかのバージョンや種類がありますが、ここではRSS2.0を例として書きます。後述するfeedのREADMEでは、Atomの例が載っているので、比較するのも面白いかもしれません。

RSS2.0では「RSSタグ」の中に「channelタグ」があり、その中に複数の「itemタグ」があります。itemタグが記事1つ分のエントリに相当します。

feedモジュール

RSSの中身はただのXMLファイルなので、例えばText.XMLを使ってしまえば、RSSの生成はできちゃいますが、やはり専用のモジュールを使った方が、わかりやすく型安全になるというものです。

Haskellでは、RSS向けに「feed」というモジュールがあります。ある程度のRSSの種類に対応しているようです。

トップレベル(RSS -> Text)

まずは、RSS型の紹介です。

ちゃんとした型定義は公式をみてもらうとして、こんな感じで作ってやります。時刻はRFC822形式なので、Data.Timeのrfc822DateFormatを使ってあげるといいでしょう。

import Data.Text as T

myRSS :: UTCTime -> RSS
myRSS now =
  let ver = "2.0"
      attrs = []
      description = "My RSS description"
      title = "My RSS title"
      link = "http://example.com"
      channel = RSSChannel
        { rssTitle = title
        , rssLink = link
        , rssDescription = description
        , rssItems = []
        , rssLanguage = Nothing
        , rssCopyright = Nothing
        , rssEditor = Nothing
        , rssWebMaster = Nothing
        , rssPubDate = Nothing
        , rssLastUpdate = Just $ T.pack $ formatTime defaultTimeLocale rfc822DateFormat now
        , rssCategories = []
        , rssGenerator = Nothing
        , rssDocs = Nothing
        , rssCloud = Nothing
        , rssTTL = Nothing
        , rssImage = Nothing
        , rssRating = Nothing
        , rssTextInput = Nothing
        , rssSkipHours = Nothing
        , rssSkipDays = Nothing
        , rssChannelOther = []
        }
  in
    RSS ver attrs channel []

わざわざ全部書かなくても、nullRSSを使うとRSS型が手軽にできますが、最初はこのように「どのようなフィールドがあるのか」がわかったほうが理解は早いと思います。

この例では、まだアイテムは何もないですが、このようにして作ったRSS型はtextRSS関数でXMLなテキストになります。

xmlText :: UTCTime -> Maybe Text
xmlText now = textRSS $ myRSS now

戻り値の型はLazyなTextなので、正格にするかは出力のインターフェイスに合わせてあげましょう。あと、戻り値の型がTextではなくMaybe Textになっているのは、Text.XMLのレンダリング関数を使っているためにそうなっているのではないかと思います。RSS型を食わせてやる限り、必ずJustになるのでないかなぁ、と(裏はとりきってないですが)。

アイテム(RSSItem)

肝心のアイテムは、RSS型のrssItemsにセットします。型はRSSItemです。そのまんまですね。下記はあくまで例です。RSSItemのメンバはすべてMaybe aか[a]なので、「何も指定しないRSSItem」も作れます。意味はないですが。RSSItemを好きな数だけ作って、リストとしてRSSChannelのrssItemsにセットしてあげましょう。

myRSSItem :: Text -> URLString -> UTCTime -> Text -> RSSItem
myRSSItem title link timestamp desc =
    RSSItem
    { rssItemTitle = Just title
    , rssItemLink = Just link
    , rssItemDescription = Just desc
    , rssItemAuthor = Nothing
    , rssItemCategories = []
    , rssItemComments = Nothing
    , rssItemEnclosure = Nothing
    , rssItemGuid = Nothing
    , rssItemPubDate = Just $ T.pack $ formatTime defaultTimeLocale rfc822DateFormat timestamp
    , rssItemSource = Nothing
    , rssItemAttrs = []
    , rssItemOther = []
    }

さいごに

ざっくり書いたので、コードにミスとかあるかも。
でも、ミスが有ってもHaskellerならどうにかできるでしょ(てきとう)

Atomとかでやりたい人がいたら、公式のREADMEとかを見よう。きっとできる。

…といいつつ、個人的にはちょっとわかりづらかったので、RSS2.0をやって、さらに記事まで書いたんですが。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?