複数執筆者による共有Feedの解析と更新管理 Part1

これは2009/10/21にAmebloに投稿した記事です。

1つのFeedで複数ユーザーのブログやコラムを配信するサイト

このameblo(アメブロ)もそうですが、一般的なブログでは、
個々人のブログ更新情報を配信するWebFeed(フィード RSS/Atomなど)はそれぞれの1個人専用のものとなります。

一方、複数の人物のブログや複数のライターのコラムなどを1ページ(1コーナー)として纏め、
そのページ全体で1つのFeedを配信するような形態のWebサイトもそれなりの頻度で見掛ける事が出来ます。
そのような複数ユーザー共有Feedのブログの場合には、Feedで配信された更新記事は一体誰が書いたのか、
あるいはその更新情報の扱いをどうするか、
というように1ブログ=1Feedの関係とはまた違った管理を行う必要が有ります。

そこでこのような案件は、
他のサイトが発信するデータの取得とサーバー側スクリプトでのデータ解析と処理、
そのデータのDBでの管理方法というように、多数の要素が絡むいかにもWebアプリケーションらしいネタという訳で、
ブログ情報サイトBlogara:ブロガラでは、
そのような構成のFeedの取得と管理をどのように行うようになったかという過程と共に解説します。

指針

  • 環境は、PHP5.2.x, MySQL5.1.x
  • WebFeedは、Pull型として各Feed配信サーバーに定期的に問い合わせ取得する
  • 各ブログテーブルはレスポンスヘッダで返されるlastModifiedとETagを保持するカラムを持ち、それをリクエストヘッダで送る事で不必要なFeedデータの解析を行わない
  • ユーザーは複数のブログを所持し、各ブログは1つの更新情報(タイトル/本文/更新日)レコードを所有する
  • 複数ユーザー共有Feedのブログでも、各ブログは独立したレコードとそれぞれの更新情報レコードを所有する
  • 共有Feedブログでは、各更新処理中に1度だけ共有Feedのリクエストを送り、その1つのFeedデータから共有する全てのユーザーの更新情報を取得する

複数人が執筆するWebFeedの取得とデータ解析

今更書くまでも無い、他のサイトが配信するFeedを取得する方法

情報配信に利用されるFeed(大抵の場合RSS/AtomのようなXML形式)は、
HTTP経由で配信元サイトから取得出来ますので、各言語のライブラリを利用して簡単に行う事が可能です。
# ただ、XMLデータを返すのにも拘わらず、レスポンスヘッダのContent-Typetext/htmlだったりする変態サイトや、
# HTTP1.1のTransfer-Encoding: chunkedの扱いに微妙にクセが有るWebサーバーが存在しますので注意が必要です。
# chunkedに関してはまた別の機会にこのブログのネタとして取り上げる予定。


更新記事は"誰が"書いたのか

Feedで配信される更新情報では、通常新しい日付の記事から並ぶという時系列順となっています。
Blogaraでは、個々人のブログの最新更新1件分だけの情報を保持していますので、
個人専用のFeedならば、単純に最初に記述される最新(日付の)記事だけを取得すれば良いのですが、
複数人による共有Feedでは、それぞれの執筆者についての最新記事を抽出する必要が有ります。

幸い、現在Blogaraで対応しているRSS1.0/RSS2.0/Atom1.0のいずれのFeedにおいても、
執筆者を指定する用途に利用出来るエレメントが規定されていますので、
それらのエレメントを使って個々の記事において執筆者を特定しているならば、
大抵の場合、複数ユーザー共有Feedとして利用出来ます。
RSS1.0ではDublin Coreモジュール(NameSpace:dc)のdc:creatorエレメント、
RSS2.0ではauthorcategoryAtom1.0ではauthorなどが、
複数ユーザー共有Feedでの個々の執筆者指定に使われる事が多くなります。


一例を挙げると、テーマ: アイドリング!!!に登録されているアイドリング!!!ブログ「煮詰まります!!!」では、
それぞれのメンバーのブログ記事を1つのFeed(RSS1.0)に纏めて配信しています。
そしてそのFeedでは、個々の記事更新データ(item)に
<dc:creator>14号:酒井瞳</dc:creator>のようなサブエレメントを追加する事で、執筆者の指定を行っています。
あるいは、テーマ: NFLNFL Japanの公式サイトに連載されているNFL JAPAN コラムのFeed(RSS2.0)では、
個々のitem内の<category>近藤 祐司</category>によって執筆者が指定されています。


PHPでのFeed(XML)からの更新情報データ抽出

複数ユーザー共有Feedにおいても、個々のユーザーそれぞれの更新情報を識別出来る事は判りましたので、
次はPHPで実際にどのようにFeedから更新情報を抽出するかになりますが、
Blogaraで扱う3種類のFeedは全てXMLですので、FeedからのというよりもXMLからのデータ抽出操作となります。

正直、この時点までPHPXMLデータ操作を行った事が無かったので、
複数あるXML操作クラス/関数のどれを使うか考えましたが、
HTMLとXMLに対応し、JavaScriptでも馴染みのあるDOM操作が可能という事で、
PHP5のDOMDocumentを利用してみる事にします。


各Feedでは、個々の更新情報が特定のエレメント(RSS1.0/2.0はitem、Atom1.0はentry)として列挙され、
個々の記事のタイトル/内容/更新日などの情報はそのエレメントの子エレメントとして記載されています。
DOMDocumentクラスには、
ノード名を指定して出現順にエレメントを取得出来るgetElementsByTagNameメソッドがありますので、
それを利用する事で簡単に更新情報リストを取得出来、
1ユーザー専用Feedの場合には、そのリストの先頭のエレメントが最新の更新記事1件の内容となります。

と、このように専用Feedでは特に何も考えずこれで問題無いのですが、
共有Feedの場合はそれほど単純な話では有りません。
共有Feedでの執筆者指定は、上記で説明したようにそれぞれのFeedで異なり、
さらにそれらは単に推奨されているだけなので、
イレギュラーなFeedを配信するサイトが存在する事も十分考えられます。


共有Feedでの個々の執筆者の更新情報取得処理としては、
まずitem/entry下の何のノードの何と言う文字列で指定されているのかという事をブログの登録時に記述し、
getElementsByTagNameで取得した更新記事リストの内容を漁り、
執筆者特定設定に一致する最初のitem/entryから記事内容を取得する、というようなものになるだろうと考えました。
が、これでは専用Feedの処理とは別の処理となり、
さらに執筆者特定設定の記述が独自仕様の特殊なものになると思われますので、あまり芳しく有りません。

そこで、専用/共有Feedデータ抽出を可能な限り共通処理にする事と、
ブログ設定での執筆者特定記述には何か標準的な文法に従った文字列にするという事を考え、
(遊びで;)Greasemonkeyスクリプトを書いていた時に多少かじったXPathを使ってみる事にします。


DOMXPath

XPathとは何なのか、あるいはどのように記述するのかはネット上のドキュメントをご覧下さい。
W3Cのドキュメントこことかここなどが参考になると思われます。


PHPでのXPathとなると、SimpleXMLの利用も考えられますが、
今回はDOMDocumentクラスと共に使うDOMXPathクラスを選択します。

ちなみにDOMXPathについてもPHPのマニュアルを見てもらった方が話が早いですが、
DOMXPathの場合にはマニュアルだけではなく、付記されているユーザコメントを読む事を強くお勧めします。
大抵のFeedXMLには、xmlns="http://purl.org/rss/1.0/" やら xmlns="http://www.w3.org/2005/Atom" などの
defaultのnameSpace(名前空間)が設定されていると思われるので、
その場合には"必ず" DOMXPathのregisterNamespaceでprefixとdefaultのnameSpaceを登録するようにして下さい。


XPathの利用により、専用/共有のどちらのFeedにおいても、
いちいちgetElementsByTagNameでエレメントを取得してから云々という処理は必要無く、
XPathのquery一発で最新更新記事の内容を保持したエレメント群を取得する事が可能になります。

// 専用Feed(Atom)の場合には、
$nodeList = $xpath->query( "//atom:item[1]/*" )

// 一方、共有Feedのアイドリング!!!煮詰まりますでは、 $nodeList = $xpath->query( "//rss1:item[dc:creator='14号:酒井瞳'][1]/*" ) // また、共有FeedのNFL Japanのコラムでは、 $nodeList = $xpath->query( "//item[category='近藤 祐司'][1]/*" );

というように、XPath式が異なるだけで専用/共有Feedの区別無く非常に簡潔な処理となり、
また、共有Feedでの執筆者特定記述もXPath式となりますので、判りやすい?/テストし易いものとなっています。


Part2に続く

ここまでで、共有Feedからの個別執筆者毎の最新更新記事抽出処理は完了しましたので、
次は、Feedを共有する複数ブログの管理などを行います。

参考資料

W3C: XML Path Language (XPath)
XPath 教程
w3schools: XPath