Hey Say、RSS使ってるかい?
普段、FeedlyとかFeedlyとかFeedlyとかでRSSを読んでいるエンジニア諸君、**「自分でRSSリーダーを作ろうと思ったことはないかね?」。**僕はありません。
でもお仕事でこういう内容を実装することはあると思います。
ということで本日は、Rails(Ruby)で使えるParserやらgemやらを通じてココらへんの処理について見ていきます。
そもそもRSSって一種類じゃないよね
僕らが普段何気に使っているRSS(ここでは広義にサイトの更新情報をフィードで提供するものとする)ですが、実はこれには規格が幾つか存在します。
- RSS 1.0
- RSS 2.0
- Atom
RSSは他にもいくつか0.9x系も存在するみたいだけど、とりあえず上記の3つを抑えておけば問題ないと思われる。(多分ね
RSS1.0のサンプル
RDF Site Summary (RSS) 1.0 日本語訳より拝借。
実際に欲しいフィード情報は、rdf>item
内に含まれている。実際に取れそうな情報は、title
、link
、description
辺りか。
<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
>
<channel rdf:about="http://www.xml.com/xml/news.rss">
<title>XML.com</title>
<link>http://xml.com/pub</link>
<description>
XML.com features a rich mix of information and services
for the XML community.
</description>
<image rdf:resource="http://xml.com/universal/images/xml_tiny.gif" />
<items>
<rdf:Seq>
<rdf:li resource="http://xml.com/pub/2000/08/09/xslt/xslt.html" />
<rdf:li resource="http://xml.com/pub/2000/08/09/rdfdb/index.html" />
</rdf:Seq>
</items>
<textinput rdf:resource="http://search.xml.com" />
</channel>
<image rdf:about="http://xml.com/universal/images/xml_tiny.gif">
<title>XML.com</title>
<link>http://www.xml.com</link>
<url>http://xml.com/universal/images/xml_tiny.gif</url>
</image>
<item rdf:about="http://xml.com/pub/2000/08/09/xslt/xslt.html">
<title>Processing Inclusions with XSLT</title>
<link>http://xml.com/pub/2000/08/09/xslt/xslt.html</link>
<description>
Processing document inclusions with general XML tools can be
problematic. This article proposes a way of preserving inclusion
information through SAX-based processing.
</description>
</item>
<item rdf:about="http://xml.com/pub/2000/08/09/rdfdb/index.html">
<title>Putting RDF to Work</title>
<link>http://xml.com/pub/2000/08/09/rdfdb/index.html</link>
<description>
Tool and API support for the Resource Description Framework
is slowly coming of age. Edd Dumbill takes a look at RDFDB,
one of the most exciting new RDF toolkits.
</description>
</item>
<textinput rdf:about="http://search.xml.com">
<title>Search XML.com</title>
<description>Search XML.com's XML collection</description>
<name>s</name>
<link>http://search.xml.com</link>
</textinput>
</rdf:RDF>
RSS2.0のサンプル
RSS 2.0 Specification 日本語訳より拝借。
こちらはrss>channel>item
内に含まれてる。実際に取れそうな情報は、title
、link
、description
、pubDate
あたり。
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Liftoff News</title>
<link>http://liftoff.msfc.nasa.gov/</link>
<description>Liftoff to Space Exploration.</description>
<language>en-us</language>
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
<lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>Weblog Editor 2.0</generator>
<managingEditor>editor@example.com</managingEditor>
<webMaster>webmaster@example.com</webMaster>
<item>
<title>Star City</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
<description>How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>.</description>
<pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
</item>
<item>
<description>Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">partial eclipse of the Sun</a> on Saturday, May 31st.</description>
<pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid>
</item>
<item>
<title>The Engine That Does More</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp</link>
<description>Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that.</description>
<pubDate>Tue, 27 May 2003 08:37:32 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/27.html#item571</guid>
</item>
<item>
<title>Astronauts' Dirty Laundry</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link>
<description>Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options.</description>
<pubDate>Tue, 20 May 2003 08:56:02 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid>
</item>
</channel>
</rss>
Atomのサンプル
RFC 4287 The Atom Syndication Format 日本語訳より拝借。
こちらはfeed>entry
内に含まれてる。実際に取れそうな情報は、title
、link
、summary
、updated
あたり。
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Feed</title>
<link href="http://example.org/"/>
<updated>2003-12-13T18:30:02Z</updated>
<author>
<name>John Doe</name>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<entry>
<title>Atom-Powered Robots Run Amok</title>
<link href="http://example.org/2003/12/13/atom03"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated>
<summary>Some text.</summary>
</entry>
</feed>
それぞれ構造、使えそうな情報に差があるようだ。
実データを見てみる
ちょうどいいことに、技術評論社が3パターンでの配信を行っているので、確認してみる。
まぁ大体上記のサンプルと同等の構造をとってますね。ここまではわかった。じゃあ本題に移ろう。
取得してパースしてみる
今回の目的は、「サクッとRSS(Atom)をパースする」ことである。便利なことに、to_feed
で好きなfeedに変換でき(そうな)メソッドが定義されているようで、RSS1.0でもRSS2.0でもAtomでも、なんでもかんでもいい感じによろしくしてくれそうである。
ものは試しに、こんな感じのコードを走らせてみる。
require 'rss'
sites = [
"http://gihyo.jp/feed/rss1",
"http://gihyo.jp/feed/rss2",
"http://gihyo.jp/feed/atom"
]
sites.each do |site|
rss = RSS::Parser.parse(site, false).to_feed('atom1.0')
rss.items.each do |item|
puts item.title
puts item.link
puts item.description
puts
end
end
ダメだった。
やっぱり定義されてないタグがあると、怒られるようだ。to_feed
の意味よ。
自前で各仕様ごとにパラメータを分けた処理を書いてもいいのだが、やっぱり凡人の僕は楽をしたいもんである。車輪の再発明はしたくない。
gemを試してみる
世の中そううまくはいかないことがわかったので、今度はgem
を頼ってみる。
Ruby ToolboxとGoogle先生の情報を頼りに、feedjira/feedjiraが良さそうだったので、試してみる。
gemを入れて、
gem 'feedjira'
ざっくりこんな感じで書いてみる。ソースが汚いのはご愛嬌。
sites = [
"http://gihyo.jp/feed/rss1",
"http://gihyo.jp/feed/rss2",
"http://gihyo.jp/feed/atom"
]
@list = []
sites.each do |site|
rss = Feedjira::Feed.fetch_and_parse site
rss.entries.each do |item|
@list += [:title => item.title,
:url => item.url,
:summary => item.summary,
:published => item.published.to_time.strftime("%Y-%m-%d %H:%M:%S")]
end
end
@list
出力はこうなる。
[60]
0: {
"title": "第440回 LibreOffice Writerの日本語レイアウト ── Ubuntu Weekly Recipe"
"url": "http://gihyo.jp/admin/serial/01/ubuntu-recipe/0440"
"summary": "今回は,LibreOffice Writerを日本語ワードプロセッサとして使用する場合に,知っておきたい日本語レイアウトの設定について解説します。"
"published": "2016-10-05 10:36:00"
}-
1: {
"title": "第2回 トラブルに備える ── Javaでなぜ問題が起きるのか 〜システムをきちんと運用するための基礎知識"
"url": "http://gihyo.jp/dev/serial/01/java-system-operation/0002"
"summary": "本連載のテーマである「運用」は,システムがリリースしてからおこなわれるものです。では,開発者には運用の知識は必要ないでしょうか?"
"published": "2016-10-05 10:20:00"
}-
2: {
"title": "2016年10月5日 「最初の設計がダメすぎる」― systemdをめぐる終わりなき論争 ── Linux Daily Topics"
"url": "http://gihyo.jp/admin/clip/01/linux_dt/201610/05"
"summary": "Fedora,Ubuntu,Debianなど,いまやほとんどのメジャーなLinuxディストリビューションではsystemdが最初の起動処理システムとして採用されている。"
"published": "2016-10-05 09:30:00"
}-
(以下略)
RSS1.0、RSS2.0、Atom、同じプロパティ名で取得できました!
ソースを見ると、いい感じにラップしてくれていて、同じプロパティ名で取得できるようにしてくれているようです。これこれ、こういうのを待っていました。
表示だけならリアルタイムで出せばいいし、アプリ側に保存したいならfind_or_create_by
みたいなんをいい感じに使えばいいんじゃないかな(まだ考えきれてない。
ご意見ご要望承ります。