Perl でフィード (Atom) を作成する方法

公開日:
更新日:
0feed

フィードとは

フィード (feed) とは、Web サイトのコンテンツについて概要や更新時間などを記録したドキュメント、またはそのファイルフォーマットのことを指します。より具体的にはニュースフィードや、ウェブフィードなどと呼ぶこともあります。フィードの代表的なフォーマットとしては RSS や Atom があります。


フィードのメリットは、Web サイトの更新タイミングをユーザが検知できる点です。一般的なフィードの使い方としては、ユーザが Web サイトの情報がまとめられたフィードを自身のフィードリーダーに登録します。Web サイトに更新があればフィードに通知されるため、ユーザは最新のコンテンツを効率的に得ることができます。

Atom のフォーマット

以下は、Atom のフォーマット例です。


<?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>
 Atom のフォーマット例

Atom のフィードは大きく分けて "feed""entry" に分けられます。feed は全体の要素を記述し、entry は個別の記事の要素を記述します。また、要素は "コンテナ要素""メタデータ要素" に分けられます。コンテナ要素とは、feed や entry のように他のメタデータ要素を包む要素を指します。

atom:feed 要素

atom:feed 要素は Atom フィードのドキュメント要素 (トップレベルの要素) です。フィードに関連付けられたメタデータと、データ用のコンテナとしての役割を持ちます。この要素を親要素としたとき、子要素は 0 または 1 つ以上の atom:entry 子要素から成り立ちます。


atomFeed =
    element atom:feed {
        atomCommonAttributes,
        (atomAuthor*
          & atomCategory*
          & atomContributor*
          & atomGenerator?
          & atomIcon?
          & atomId
          & atomLink*
          & atomLogo?
          & atomRights?
          & atomSubtitle?
          & atomTitle
          & atomUpdated
          & extensionElement*),
        atomEntry*
    }
 atom:feed 要素の定義
atom:feed 要素の構造
要素名定義
atom:feed1 つ以上の atom:author 要素を含まなければいけません。ただし、atom:feed 要素のすべての子要素 atom:entry 要素に 1 つ以上の atom:author 要素が含まれる場合は、その限りではありません。
atom:category0 個以上含めることができます。
atom:contributor0 個以上含めることができます。
atom:generator0 個 または 1 つだけ含めることができます。
atom:icon0 個 または 1 つだけ含めることができます。
atom:id必ず 1 つだけ含めなれけばいけません。
atom:link0 個以上含めることができます。
atom:logo0 個 または 1 つだけ含めることができます。
atom:rights0 個 または 1 つだけ含めることができます。
atom:subtitle0 個 または 1 つだけ含めることができます。
atom:title必ず 1 つだけ含めなれけばいけません。
atom:updated必ず 1 つだけ含めなれけばいけません。

atom:entry 要素

atom:entry 要素は個別のエントリを表し、そのエントリに関連付けられたメタデータと、データ用のコンテナの役割を持ちます。


atomEntry =
    element atom:entry {
        atomCommonAttributes,
        (atomAuthor*
          & atomCategory*
          & atomContent?
          & atomContributor*
          & atomId
          & atomLink*
          & atomPublished?
          & atomRights?
          & atomSource?
          & atomSummary?
          & atomTitle
          & atomUpdated
          & extensionElement*)
    }
 atom:entry 要素の定義
atom:feed 要素の構造
要素名定義
atom:entry1 つ以上の atom:author 要素を含まなければいけません。ただし、atom:feed 要素のすべての子要素 atom:entry 要素に 1 つ以上の atom:author 要素が含まれる場合は、その限りではありません。
atom:category0 個以上含めることができます。
atom:content0 個 または 1 つだけ含めることができます。
atom:contributor0 個以上含めることができます。
atom:id必ず 1 つだけ含めなれけばいけません。
atom:link0 個以上含めることができます。
atom:published0 個 または 1 つだけ含めることができます。
atom:rights0 個 または 1 つだけ含めることができます。
atom:source0 個 または 1 つだけ含めることができます。
atom:summary0 個 または 1 つだけ含めることができます。
atom:title必ず 1 つだけ含めなれけばいけません。
atom:updated必ず 1 つだけ含めなれけばいけません。

Perl でフィード (Atom) を作成する

Perl でフィード (Atom) を作成するには "XML::Atom" モジュールを利用すると作成が楽になります。以下が Perl でフィード (Atom) を作成するサンプルコードです。ただし、さくらインターネットの場合は標準でインストールされているモジュールの中に Data::UUID が含まれていないため、エラー (Can't locate Data/UUID.pm in @INC) になります。Data::UUID を使えるようにする手順は後述します。


#!/usr/bin/perl
 
use strict;
use warnings;
use DateTime;
use XML::Atom::Feed;
use XML::Atom::Link;
use XML::Atom::Entry;
use Data::UUID qw(NameSpace_URL);

my $feed = XML::Atom::Feed->new(Version => 1.0);

# UUIDを追加
my $ug = Data::UUID->new;
my $urnid = $ug->create_from_name_str(NameSpace_URL, "murashun.jp");
$feed->id('urn:uuid:' . $urnid);

# Titleを追加
$feed->title('murashun.jp/atom.xml');

# Languageを追加
$feed->language('ja');

# linkを追加
my $link = XML::Atom::Link->new(Version => 1.0);
$link->rel('self');
$link->type('application/atom+xml');
$link->href('https://murashun.jp/atom.xml');
$feed->add_link($link);

$link = XML::Atom::Link->new(Version => 1.0);
$link->rel('hub');
$link->href('http://pubsubhubbub.appspot.com');
$feed->add_link($link);

# authorを追加
my $author = XML::Atom::Person->new(Version => 1.0);
$author->name('murashun');
$author->email('webmaster@murashun.jp');
$feed->author($author);

# Updatedを追加
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my $datefmt = sprintf("%04d-%02d-%02dT%02d:%02d:%02d+09:00", $year + 1900, $mon + 1, $mday, $hour , $min , $sec);
$feed->updated($datefmt);

# rightsを追加
$feed->rights('Copyright ' . ($year + 1900) . ' murashun.jp');

# Feedを作る対象のディレクトリを設定
search1('../../contents/');
search1('../../blog/');

# ディレクトリの中身を走査
sub search1 {
  opendir(DIR, $_[0]);
  my @filelist = readdir(DIR);
  closedir(DIR);
  foreach my $file (@filelist) {
    if ($file !~ /.+\.s?html$/)  { next; }
    if ($file =~ /index\.html$/) { next; }
    if (-d "$_[0]/$file") {
        &search1("$_[0]$file/");
    } else {
        &search2($_[0], $file);
    }
  }
}

# 対象ファイルをEntryに追加
sub search2 {
  my @contents = ();
  open(IN, $_[0] . $_[1]);
  @contents = <IN>;
  close(IN);
    
  my $urls = "";
  my $title = "";
  my $description = "";
  
  foreach(@contents) {
    $urls = $_[0] . $_[1];
    $urls =~ s/\.\.\/\.\.\//https:\/\/murashun.jp\//;
    
    # タイトル取得
    if($_ =~ /^<title>/){
      $_ =~ s/<\/?title>|\s*\|\s*murashun\.jp|\t|\r\n|\r|\n//g;
      $title = $_;
    }
    
    # description取得
    elsif($_ =~ /^<meta name="description"/){
      $_ =~ s/<meta name="description" content="|">|\t|\r\n|\r|\n//g;
      $description = $_;
    }
  }
  
  my $entry = XML::Atom::Entry->new(Version => 1.0);
  
  # titleを追加
  $entry->title($title);
  
  # UUIDを追加
  my $buf_url = $urls;
  $buf_url =~ s/https:\/\///;
  $ug = Data::UUID->new;
  $urnid = $ug->create_from_name_str(NameSpace_URL, $buf_url);
  $entry->id('urn:uuid:' . $urnid);
  
  # updatedを追加
  my ($sec,$min,$hour,$day,$mon,$year) = localtime((stat $_[0] . $_[1])[9]);
  my $updfmt = sprintf("%04d-%02d-%02dT%02d:%02d:%02d+09:00", $year + 1900, $mon + 1, $day, $hour , $min , $sec);
  $entry->updated($updfmt);
  
  # linkを追加
  $link = XML::Atom::Link->new(Version => 1.0);;
  $link->href($urls);
  $entry->add_link($link);
  
  # summaryを追加
  $entry->summary($description);
  
  # feedに追加
  $feed->add_entry($entry, { mode => 'insert' });
}

# FeedをXML形式で出力
print $feed->as_xml;

exit(1);
 Perl でフィード (Atom) を作成するサンプルコード

完成したフィードは、以下のサイトで文法チェックができます。誤った形式のフィードを配信しないように、チェックすることを推奨します。


FEED Validator

モジュールをインストールする

モジュールをインストールするには、まず CPAN から対象のモジュールをダウンロードします。以下は、Data::UUID を例に説明しています。


Data::UUID - search.cpan.org

ダウンロードが終わったら、ダウンロードしたファイルをサーバの任意の場所に配置します。次に CUI でサーバにログインし、ダウンロードしたファイルを配置したディレクトリまで移動します。その後、以下のコマンドを順番に実行することでインストールが完了します。ただし、Makefile を実行する手順において "PREFIX" に指定するパスは、ダウンロードファイルを配置したパスを指定して下さい。


# Data-UUID-1.221.tar.gz を解凍する
% tar zxvf Data-UUID-1.221.tar.gz
・・・ (中略) ・・・

# ディレクトリを移動する
% cd Data-UUID-1.221

# Makefile を実行する
% perl Makefile.PL PREFIX=/home/murashun/cpan/
・・・ (中略) ・・・

# make test コマンドを実行する
% make test
・・・ (中略) ・・・

# make install コマンドを実行する
% make install
・・・ (中略) ・・・
 モジュールをインストールするコマンド実行例

インストール完了後、モジュールを使うにはプログラム内にパスを指定する必要があります。インストールが正常に完了していれば、ダウンロードファイルを配置したディレクトリに配下に /lib/perl5/site_perl/5.14/mach と続くディレクトリができています。そのパスを絶対パスで、以下のように指定することで対象のモジュールが使用可能になります。


use lib qw(/home/murashun/cpan/lib/perl5/site_perl/5.14/mach);
use Data::UUID qw(NameSpace_URL);
 モジュールをインストール完了後に指定するパス

まとめ

フィードはユーザに更新タイミングを通知するために役に立ちますが、Google の検索エンジンにも通知できます。似ている仕組みはサイトマップで実施していますが、サイトマップはクローラーにサイトの構造のヒントを提供しており、フィードは更新タイミングを提供しています。そのため、Google でもサイトマップとフィードの両方を使うことを推奨しています。フィードが完成したら、ウェブマスターツールのサイトマップに登録することで受理されます。


また、フィード (Atom) は Google が推奨する PubSubHubbub に対応しているため、PubSubHubbub に送るとより効果的です。PubSubHubbub については、PubSubHubbub で更新情報を Google にリアルタイムで通知する方法を参照して下さい。