FeedjiraでRSSの独自タグ拡張
feedjira gemを利用した独自タグ拡張の方法について説明します。
Feedjiraには add_common_feed_entry_element
というメソッドがあります。
https://www.rubydoc.info/github/feedjira/feedjira/master/Feedjira/Feed#add_common_feed_entry_element-class_method
ドキュメントがなくコードを読んでも
https://github.com/feedjira/feedjira/blob/master/lib/feedjira/feed.rb#L18
だいぶ深追いしないと把握できないのでとりあえずどういう動きをするのかを説明します。
シンプルな構造の場合(ネストしていない)
以下のような my:tag
という独自タグが含まれるRSSがあったとします
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
>
<channel>
<title>サイトタイトル</title>
<link>https://example.com/</link>
<description>サイト説明</description>
<pubDate>Mon, 19 Jun 2020 09:00:00 7JunJST</pubDate>
<language>ja</language>
<copyright>誰か</copyright>
<item>
<title><![CDATA[タイトル]]></title>
<link>https://example.com/item/1</link>
<guid>https://example.com/item/1</guid>
<description><![CDATA[ディスクリプション]]></description>
<pubDate>Mon, 19 Jun 2020 02:55:52 2JunJST</pubDate>
<content:encoded><![CDATA[
コンテンツ本文
]]></content:encoded>
<!-- 独自タグ -->
<my:tag link="https://example.com/" title="example">独自タグ</my:tag>
<!-- /独自タグ -->
</item>
</channel>
</rss>
独自タグ部分をparseしてFeedjiraのentryオブジェクトに追加したい場合
<my:tag link="https://example.com/" title="example">独自タグ</my:tag>
普通にFeedjira::parse
しただけでは取得できません。
そこで add_common_feed_entry_element
の登場です。
Feedjira::Feed.add_common_feed_entry_element("my:tag", as: :my_tag)
このように書くことでFeedjiraのentryオブジェクトにmy_tagという名前で追加されタグのinner textを取得することが出来ます。
Feedjira::Feed.add_common_feed_entry_element("my:tag", :as => :my_tag)
resp = Faraday.get('http://example.com/feed')
feed = Feedjira.parse(resp.body)
feed.entries.each do |entry|
puts entry.my_tag
# stdout: 独自タグ
end
link
アトリビュートや title
アトリビュートが必要な場合は value
にアトリビュート名を設定します。
Feedjira::Feed.add_common_feed_entry_element("my:tag", as: :my_tag)
Feedjira::Feed.add_common_feed_entry_element("my:tag", as: :my_tag_title, value: :title)
Feedjira::Feed.add_common_feed_entry_element("my:tag", as: :my_tag_link, value: :link)
resp = Faraday.get('http://example.com/feed')
feed = Feedjira.parse(resp.body)
feed.entries.each do |entry|
puts entry.my_tag
# stdout: 独自タグ
puts entry.my_tag_title
# stdout: example
puts entry.my_tag_link
# stdout: https://example.com/
end
ネストしていないシンプルな構造の独自タグの場合は add_common_feed_entry_element
で簡単に取得することが出来ます。
複雑な構造の場合(ネストしている)
以下のようなRSSがあったとします。
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
>
<channel>
<title>サイトタイトル</title>
<link>https://example.com/</link>
<description>サイト説明</description>
<pubDate>Mon, 19 Jun 2020 09:00:00 7JunJST</pubDate>
<language>ja</language>
<copyright>誰か</copyright>
<item>
<title><![CDATA[タイトル]]></title>
<link>https://example.com/item/1</link>
<guid>https://example.com/item/1</guid>
<description><![CDATA[ディスクリプション]]></description>
<pubDate>Mon, 19 Jun 2020 02:55:52 2JunJST</pubDate>
<content:encoded><![CDATA[
コンテンツ本文
]]></content:encoded>
<!-- ここから独自拡張 -->
<my:nested_tag>
<my:child link="https://exmaple.com/1" title="example1" />
<my:child link="https://exmaple.com/2" title="example2" />
<my:child link="https://exmaple.com/3" title="example3" />
</my:nested_tag>
<!-- /ここまで独自拡張 -->
</item>
</channel>
</rss>
このような場合は add_common_feed_entry_element
を普通に使用しただけでは思った通りに取得が出来ません。
Feedjira::Feed.add_common_feed_entry_element("my:nested_tag", as: :nested_tag)
# valueを指定しないとinner textを返すのでブランクになる
Feedjira::Feed.add_common_feed_entry_element("my:child", as: :sponsored_link, value: :link)
# my:childから取得を試みた場合は3件のうち1件目のみが取得できる
どうするか?
add_common_feed_entry_element
には class
オプションにparserを渡せる仕組みがあります。
ドキュメントにはありませんが以下のコードから class
を渡せるということがわかります。
公式のコードを参考にして独自parserクラスを定義
module Feedjira
module Parser
class MyNestedTag
include SAXMachine
elements :"my:child", as: :link, value: :link
elements :"my:child", as: :title, value: :title
end
end
end
classに独自parserクラスを指定してみる
Feedjira::Feed.add_common_feed_entry_element("my:nested_tag", as: :nested_tag, class: Feedjira::Parser::MyNestedTag)
resp = Faraday.get('http://example.com/feed')
feed = Feedjira.parse(resp.body)
feed.entries.each do |entry|
entry.nested_tag.each do |ads|
puts ads.link
puts ads.title
end
# stdout:
# https://exmaple.com/ads/1
# example1
# https://exmaple.com/ads/2
# example2
# https://exmaple.com/ads/3
# example3
end
意図した通りに動きました。
parserクラスの書き方自体は公式のparserが参考になるのでそちらを参考にすると良いと思います
公式parser: https://github.com/feedjira/feedjira/tree/master/lib/feedjira/parser