AtomフィードをHTMLに変換表示する「atom2html.cgi」を作成する
概要
「atom2html.cgi」は、「feeds.cgi」と比べるとスクリプトの規模が大きいので、スクリプトが何をしているのか、まず簡単に解説してから、個々の注意点について説明していきます。動作は次のような流れになります。
- CGIモジュールの
param()
で、AtomフィードのURLを格納したatom
パラメータ値を得ます。 - CGI出力部分の最初の部分を出力しておきます。
- LWPモジュールの
get()
を使って、Atomフィードのファイルをインターネットから取得します。 - 最初のXML宣言行の
encoding
属性から、エンコーディングを取得します。 encoding
属性値のないものは、UTF-8を仮定します。- Unicode文字の16進表記があれば、UTF-8文字に変換します。
- エンコーディングがUTF-8でなければ、Encodeモジュールの
from_to()
でutf8に変換します。 - Atomフィードのファイルのタグの後にある改行と改行後の空白文字列を削除し、
pre
タグの間のデータの後にある改行を除いて、改行を含まない塊にします。 XML
タグの前後とCDATA
セクションの解析に必要な部分すべてに改行を挿入します。- Atomフィードの文字列の塊を改行で分割して、配列に格納します。
foreach
ループで配列から一行ずつ取り出し、各要素タグの開始と終了のパターンマッチを使って、各要素の値を、要素名をキーにして取得していきます。キーとなる要素名には同じものが存在するので、feed
主要素とentry
主要素に分けて、%feed
と%entry
に格納していきます。CDATA
セクションのデータは、ループの後の処理を妨害しないように実体参照エンコードします。content
要素中のpre
タグの間にあるデータ行には改行を付加していきます。- Atomフィードは
entry
主要素単位でまとまりになりますから、entry
主要素の終了タグの行で、%entry
に格納した各副要素をタブ区切りの文字列にまとめて、@entries
配列に格納していきます。 feed
主要素は1回しか現れないので、foreach
終了後に、Atomのバージョンに従って、%feed
の内容を整形して、HTML出力します。entry
主要素の内容の入った@entries
から、foreach
で一つずつ取り出しながら、タブ区切りで副要素を取り出しながら整形して、HTMLの表出力を繰り返します。- HTML出力する前に、各要素の内容に実体参照が含まれている場合には、実体参照をデコードしてから処理します。この際、content要素にpreタグがある場合には、実体参照のデコードは行いません。
以上のスクリプトの流れと次項のAtomフィードの形式を頭に入れてから、スクリプトのコメントを参考に流れを追ってみてください。原理的には、「feeds.cgi」で説明した行単位の入力のループの中で動作を変えるテキスト処理です。取得すべき要素の数が多く、しかも形態が多様であり、属性の取得等も考慮しているために、ループ中の条件判定が複雑になっています。
Atomフィードの例
スクリプトの理解を深めるためには、入力するAtomフィードのファイル内容を知る必要があります。ここで、Atomフィードの要素を説明するよりは、実際のサンプルを見るのが理解も早いでしょう。著者のサイトのAtomフィードの一部を示しておきます。entry要素を一つだけ示しています。
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>日曜プログラマのひとりごと</title> <subtitle>TS Networkのために - 更新日記</subtitle> <updated>2006-05-13T00:16:35+09:00(JST)</updated> <link rel="self" type="application/atom+xml" href="http://homepage1.nifty.com/kazuf/renewal_atom.xml"/> <link rel="alternate" type="text/html" hreflang="ja" href="http://homepage1.nifty.com/kazuf/renewal.html"/> <rights>Copyright (C) 2006 Kazuo Fujioka</rights> <generator uri="http://homepage1.nifty.com/kazuf/renewal.html" version="1.0"> rn2atom.cgi </generator> <entry> <title>atom2html.cgi v0.92リリース</title> <link rel="alternate" type="text/html" href="http://homepage1.nifty.com/kazuf/renewal.html#rn_1147266376"/> <published>2006-05-10</published> <updated></updated> <category>更新履歴</category> <author> <name>Kazuo Fujioka</name> </author> <content type="xhtml" xml:lang="ja" xml:base="http://homepage1.nifty.com/kazuf/"> <div xmlns="http://www.w3.org/1999/html"> <div class="emph"> <a href="http://homepage1.nifty.com/kazuf/renewal_index.html#rn"> [更新履歴]</a> <a name="rn_1147266376" id="rn_1147266376"> atom2html.cgi v0.92リリース</a></div> <p> 改良点は次のとおり。本スクリプトはjperl用です。 → <a href="http://homepage1.nifty.com/kazuf/atom2html.html"> atom2html</a> </p> <ul> <li>Unicode文字16進表記のUnicode文字への変換。</li> <li>CDATAセクションの実体参照の二重エンコードのデコードに対応。</li> </ul><br /> </div> </content> </entry> </feed>
Atomのフォーマットには、0.3と1.0があります。1.0の詳細は、参考資料にある『The Atom Syndication Format(RFC4287)』を参照してください。後で紹介する「atom2html.cgi」スクリプトは、0.3と1.0の両方に対応するように書いています。コメントを参考にしてスクリプトを見れば、バージョンによる要素の違いも分かります。
LWPモジュール
LWPモジュールの手軽さは特筆すべきものです。インターネット上のURLで表されるリソースを簡単に取り出すことができます。「atom2html.cgi」は、AtomフィードのURLをパラメータとして、AtomのXMLファイルを取得します。「atom2html.cgi」のスクリプトの次の部分です。
use LWP; my @header = ( 'UserAgent' => "libwww-perl/$LWP::VERSION", ); my $browser = LWP::UserAgent->new; .......... # ATOM URLとヘッダフィールドにUserAgent値を指定して ATOM ファイルを取得 my $res; unless($res = $browser->get($getfile,@header)){ print "<p>LWP::UserAgentのget
関数がundef値を返しました。 </p></BODY></HTML>\n"; exit; } my $content = $res->content;# UserAgentのget
関数の戻り値
LWP::Simpleというモジュールを使うと、引数はURLだけになり、もっと簡単になりますが、Webサイトの設定によっては、リクエストのヘッダフィールドにUserAgentの値を含めないと応答してくれない場合があります。私の経験では、libwww-perlのバージョンを送る方法で、問題なく応答が得られるようになりました。本稿とは関係ありませんが、LWP::Simpleで応答が得られない場合はこの方法を試してみるとよいでしょう。
Encodeモジュール
AtomフィードのXML宣言のencoding
属性を見て、UTF-8でなければ、該当のエンコーディングからutf8にfrom_to()
を使って変換します。次の部分です。
use Encode qw(from_to); .......... if($encoding ne "utf8"){ from_to($content,$encoding,"utf8"); }
atom2html.cgi
以下に、CGI出力画面とスクリプトを示します。
#!/Perl5.8/bin/perl.exe use strict; use warnings; use CGI qw(:cgi); use Encode qw(from_to); use LWP; my @header = ( 'UserAgent' => "libwww-perl/$LWP::VERSION", ); my $browser = LWP::UserAgent->new; # 環境変数の取得 # ローカル HTTP サーバーのドキュメントルートのフルパス my $docroot = $ENV{'DOCROOT'}; #$ENV{'CGIDIR'};# ローカル HTTP サーバーの CGI 相対ディレクトリ my $cgidir = "/cgi-bin/codezine"; my $getfile;# 取得ファイルのURL # Atom URL データの取得 if(param('atom')){ # CGI で URL を取得する $getfile = param('atom'); }elsif($ARGV[0]){ # コマンドラインから URL を取得する $getfile = $ARGV[0]; }else{ print "Atom URL データが存在しない。\n";exit;# 終了する } # CGI 最初の部分の出力 print <<HEADER; Content-type: text/html; charset=UTF-8 <html> <head> <title>ATOM Reader</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> </head> <body> <h3>Atom Reader</h3> HEADER # Feed Protocol対応 $getfile =~ s/^feed:\/\/(http:\/\/.+)$/$1/i; $getfile =~ s/^feed:(http:\/\/.+)$/$1/i; $getfile =~ s/^feed(:\/\/.+)$/http$1/i; $getfile =~ s/^feed:(.+)$/http:\/\/$1/i; # ATOM URL の ATOM ファイルを取得 my $res; unless($res = $browser->get($getfile,@header)){ print "<p>LWP::UserAgentのget関数がundef値を返しました。 </p></BODY></HTML>\n"; exit; } my $content = $res->content;# UserAgentのget関数の戻り値(ファイル内容) my $encoding = ""; # Atomファイルのエンコーディング my $enc; # Atomファイルのエンコーディングの元の文字列データ # エンコーディングの取得 if($content =~ /<\?xml +version=["']1.0["'] +encoding= ["']([^"']+)["'][^\?]*\?>/){ $encoding = $1;$enc = $encoding; $encoding =~ s/^Shift_*JIS$/sjis/i; # シフト JIS $encoding =~ s/^euc-*j*p*$/euc-jp/i;# EUC-JP $encoding =~ s/^utf-*8$/utf8/i; # UTF-8 $encoding =~ s/^ISO-8859-1$/ascii/i;# ISO-8859-1 $encoding =~ s/^us-ascii$/ascii/i; # US-ASCII $encoding =~ s/^ISO-2022-JP$/jis/i; # JIS } if($encoding eq ""){ # エンコーディング情報のないものは、UTF-8 とする # 実際のRSSファイルにはXMLを宣言する行、<?xml...?>自体がないものもある $encoding = "utf8";$enc = "None"; } # Unicode文字16進表記をUnicode文字に変換 $content =~ s/&#x(\w{4});/chr(hex($1))/eg; # エンコーディング情報をもとに、UTF-8文字コードに変換 if($encoding ne "utf8"){ from_to($content,$encoding,"utf8"); } # Atom ファイルの解析 # 解析用の前処理: 文字列データから空白を取り除き、改行文字を削除する $content =~ s/(>)[\r\n]+[ \t]*/$1/g; # 終了タグの後に改行を入れる $content =~ s/(<\/[^>]+>)/$1\n/g; # 主要素の終了タグの前に改行を入れる $content =~ s/(<\/feed>)/\n$1/g; $content =~ s/(<\/entry>)/\n$1/g; $content =~ s/(<\/author>)/\n$1/g; $content =~ s/(<\/info>)/\n$1/g; $content =~ s/(<\/content>)/\n$1/g; $content =~ s/(<\/tagline>)/\n$1/g; $content =~ s/(<\/title>)/\n$1/g; $content =~ s/(<\/summary>)/\n$1/g; $content =~ s/(<\/subtitle>)/\n$1/g; $content =~ s/(<\/pre>)/\n$1/ig;# preタグの処理 # 主要素の開始タグの後に改行を入れ、要素タグのみの行を作る $content =~ s/(<feed[^>]*>)/$1\n/g; $content =~ s/(<entry[^>]*>)/$1\n/g; $content =~ s/(<author[^>]*>)/$1\n/g; $content =~ s/(<link[^>]*>)/$1\n/g; $content =~ s/(<info[^>]*>)/$1\n/g; $content =~ s/(<content[^>]*>)/$1\n/g; $content =~ s/(<tagline[^>]*>)/$1\n/g; $content =~ s/(<title[^>]*>)/$1\n/g; $content =~ s/(<summary[^>]*>)/$1\n/g; $content =~ s/(<subtitle[^>]*>)/$1\n/g; $content =~ s/(<pre>)/$1\n/ig;# preタグの処理 # CDATAセクションの処理 $content =~ s/(<!\[CDATA\[)/\n$1\n/g; $content =~ s/(\]\]>)/\n$1\n/g; # データ文字列を改行で分割して、行単位のデータで配列に格納する my @content = split(/\n+/, $content); my $ver = "1.0";# デフォルトAtomバージョン my $line; # 行単位文字列データ my $cdatasw = 0;# cdata セクションのフラグ my $cdexist = 0;# cdata セクションの第2フラグ my $cdata; # cdataセクションデータ my $fsw = 0; # feed要素のフラグ my $isw = 0; # info要素のフラグ my $esw = 0; # entry要素のフラグ my $type; # content要素のtype属性 my $mode; # content要素のmode属性 my $consw = 0; # content要素のフラグ my $tsw = 0; # tagline要素のフラグ my $tisw = 0; # title要素のフラグ my $susw = 0; # summary要素のフラグ my $stsw = 0; # subtitle要素のフラグ my $presw = 0; # preタグ要素開始のフラグ my $preexist = 0;# preタグの第2フラグ # feed要素の副要素名をキーとする要素値の連想配列と初期化 my %feed = ( 'link', "", # 0.3, 1.0 'title', "", # 0.3, 1.0 'tagline', "", # 0.3 'modified', "", # 0.3 'info', "", # 0.3, 1.0 'subtitle', "", # 1.0 'updated', "", # 1.0 'category', "", # 1.0 'id', "", # 0.3, 1.0 'contributor', "", # 1.0 'generator', "", # 0.3, 1.0 'icon', "", # 1.0 'logo', "", # 1.0 'rights', "", # 1.0 'email', "", # 1.0 'uri', "", # 1.0 ); # entry要素の副要素名をキーとする要素値の連想配列と初期化 my %entry = ( 'issued', "", # 0.3 'published', "", # 1.0 'link', "", # 0.3, 1.0 'title', "", # 0.3, 1.0 'content', "", # 0.3, 1.0 'name', "", # 0.3, 1.0 'modified', "", # 0.3 'created', "", # 0.3 'id', "", # 0.3, 1.0 'updated', "", # 1.0 'summary', "", # 1.0 'category', "", # 0.3, 1.0 'contributor', "", # 1.0 'rights', "", # 1.0 'source', "", # 1.0 'draft', "", # 0.3 'email', "", # 1.0 'uri', "", # 1.0 ); my $buffer; # バッファ変数 my $linkattr; # link要素の属性部文字列 my $attr; # link要素の属性名 my @attrs; # link要素の属性名配列 my %lattr; # link要素の属性名をキーとする属性値の連想配列 my @entries; # %entryの配列要素をタブでつなぎ、entry単位で格納した配列 my $val; # %entryの配列要素名 my %vals; # %entryの配列要素の有無を判定するための連想配列; # データを行単位で処理する。 foreach $line (@content){ # Atom バージョンの判定 if($line =~ /<feed.+?version=["']0\.3["']/){ $ver = "0.3"; } # CDATA セクションの処理 <![CDATA[・・・]]> の除去と実体参照エンコード if($line =~ /^<!\[CDATA\[$/){ $cdatasw = 1;$cdexist = 1;next; } if($cdatasw == 1 && $line !~ /^\]\]>$/){ $cdata .= $line . "\n";# 元々あった改行の付加 next; }elsif($cdatasw == 1 && $line =~ /^\]\]>$/){ $line = &entities_encode($cdata); $cdatasw = 0;$cdata = ""; } # feed、info、entry、content、tagline、 # title、summary、subtitle 要素開始の判定 if($line =~ /<feed/){ $fsw = 1; }elsif($line =~ /<info/){ $isw = 1;next; }elsif($line =~ /<entry/){ $esw = 1;$fsw = 0;# feed 要素終了の判定 }elsif($line =~ /<content/){ if($line =~ /type=['"]([^'"]+)['"]/){ $type = $1;# type属性の取得 } if($line =~ /mode=["']([^"']+)["']/){ $mode = $1;# 0.3 } $consw = 1;next; }elsif($line =~ /<tagline/){ next; }elsif($line =~ /<title/){ $tisw = 1;next; }elsif($line =~ /<summary/){ $susw = 1;next; }elsif($line =~ /<subtitle/){ $stsw = 1;next; } # subtitle 要素終了の判定 if($stsw == 1 && $line =~ /<\/subtitle>/){ if($fsw == 1){ $feed{'subtitle'} = $buffer;$stsw = 0;$buffer = "";next; }elsif($esw == 1){ $entry{'subtitle'} = $buffer;$stsw = 0;$buffer = "";next; } } # subtitle 要素の格納 if($stsw == 1){ if($line =~ /[<>]+/){ $buffer .= &entities_encode($line); }else{ $buffer .= $line; } next; } # summary 要素終了の判定 if($susw == 1 && $line =~ /<\/summary>/){ if($fsw == 1){ $feed{'summary'} = $buffer;$susw = 0;$buffer = "";next; }elsif($esw == 1){ $entry{'summary'} = $buffer;$susw = 0;$buffer = "";next; } } # summary 要素の格納 if($susw == 1){ if($line =~ /[<>]+/){ $buffer .= &entities_encode($line); }else{ $buffer .= $line; } next; } # title 要素終了の判定 if($tisw == 1 && $line =~ /<\/title>/){ if($fsw == 1){ $feed{'title'} = $buffer;$tisw = 0;$buffer = "";next; }elsif($esw == 1){ $entry{'title'} = $buffer;$tisw = 0;$buffer = "";next; } } # title 要素の格納 if($tisw == 1){ if($line =~ /[<>]+/){ $buffer .= &entities_encode($line); }else{ $buffer .= $line; } next; } # info 要素終了の判定 if($isw == 1 && $line =~ /<\/info>/){ $feed{'info'} = $buffer;$isw = 0;$buffer = "";next; } # info 要素の格納 if($isw == 1){ if($line =~ /[<>]+/){ $buffer .= &entities_encode($line); }else{ $buffer .= $line; } next; } # content 要素終了の判定 if($consw == 1 && $line =~ /<\/content>/){ $entry{'content'} = $buffer;$consw = 0;$buffer = "";next; } # content 要素の格納 if($consw == 1){ # preタグ処理 if($line =~ /<pre>/i){ $presw = 1; } if($line =~ /<\/pre>/i){ $line .= "\n";# 元々あった改行の付加 $presw = 0;$preexist = 1; } if($presw == 1){ $line .= "\n";# 元々あった改行の付加 } $buffer .= $line; next; } # link 要素の格納 if($line =~ /<link +([^ ].+?[^ ]) *\/>/){ $linkattr = $1; @attrs = ($linkattr =~ /([^ =]+=["'][^"']+["'])/g); foreach $attr (@attrs){ if($attr =~ /^([^=]+)=["']([^"']+)["']$/){ $lattr{$1} = $2; } } # feed要素の場合の処理 if($fsw == 1){ if($lattr{'type'}){ if($lattr{'type'} eq "text/html"){ $feed{'link'} = $lattr{'href'}; $feed{'title'} = $lattr{'title'} unless($feed{'title'}); }elsif($lattr{'type'} eq "application/atom+xml"){ $feed{'link'} = $lattr{'href'}; $feed{'title'} = $lattr{'title'} unless($feed{'title'}); } }else{ $feed{'link'} = $lattr{'href'}; $feed{'title'} = $lattr{'title'} unless($feed{'title'}); } } # entry要素の場合の処理 if($esw == 1){ if($lattr{'type'}){ if($lattr{'type'} eq "text/html"){ $entry{'link'} = $lattr{'href'}; $entry{'title'} = $lattr{'title'} unless($entry{'title'}); }elsif($lattr{'type'} eq "application/atom+xml"){ $entry{'link'} = $lattr{'href'}; $entry{'title'} = $lattr{'title'} unless($entry{'title'}); } }else{ $entry{'link'} = $lattr{'href'}; $entry{'title'} = $lattr{'title'} unless($entry{'title'}); } } @attrs = ();%lattr = ();$linkattr = "";$attr = ""; next; } # 属性を持たない要素データを要素毎に要素名をキーとして取得 if($line =~ /<[^>]+>(.+?)<\/([^>]+)>/){ # feed 要素の格納 if($fsw == 1){ $feed{$2} = $1; } # entry 要素の格納 if($esw == 1){ $entry{$2} = $1; } } # entry 要素終了の判定と entry ごとのデータの格納 if($esw == 1 && $line =~ /<\/entry>/){ if($entry{'issued'}){ $entry{'published'} = $entry{'issued'}; } # 表示要素追加時チェック部(1) push(@entries, join("\@\@\@",$entry{'published'},$entry{'link'}, $entry{'title'},$entry{'content'},$entry{'name'},$entry{'modified'}, $entry{'created'},$entry{'id'},$entry{'updated'},$entry{'summary'}, $entry{'category'})); $esw = 0; foreach $val (keys (%entry)){ if($entry{$val}){ $vals{$val} = 1; } } # 表示要素追加時チェック部(2) %entry = ( 'issued', "", 'published', "", 'link', "", 'title', "", 'content', "", 'name', "", 'modified', "", 'created', "", 'id', "", 'updated', "", 'summary', "", 'category', "", ); } } # feed 要素の info 要素を実体参照デコードしておく $feed{'info'} = &entities_decode($feed{'info'}); if($ver eq "0.3"){ print "<dl><dt><a href=\"",$feed{'link'},"\" target=\"main\">", $feed{'title'}, "</a></dt><dd><p>",$feed{'tagline'}," [", $feed{'modified'},"]</p><p>",$feed{'info'},"</p></dd></dl>\n"; }else{ print "<dl><dt><a href=\"",$feed{'link'},"\" target=\"main\">", $feed{'title'}, "</a></dt><dd><p>",$feed{'subtitle'}," [", $feed{'updated'},"]</p><p>",$feed{'summary'},"</p></dd></dl>\n"; } # 表示要素追加時チェック部(3) my $entry; # entry要素の副要素をタブでつないだ文字列データ my $published; # entry要素のpublished要素の値 my $link; # entry要素のlink要素の値 my $title; # entry要素のtitle要素の値 my $econtent; # entry要素のcontent要素の値 my $name; # entry要素のname要素の値 my $modified; # entry要素のmodified要素の値 my $created; # entry要素のcreated要素の値 my $id; # entry要素のid要素の値 my $updated; # entry要素のupdated要素の値 my $summary; # entry要素のsummary要素の値 my $category; # entry要素のcategory要素の値 my $titlelink; # title要素にリンクがある場合の処理文字列 my $linkhead; # title要素がない場合に作成するタイトル文字列 # entry 要素データを HTML 表に変換して出力 print "<table border=0>\n"; # 表示要素追加時チェック部(4) print "<tr bgcolor=\"#eeeeee\"><th>タイトル</th><th>記事</th> <th>日付/更新</th><th>著者</th><th>カテゴリ</th></tr>\n"; # entry ごとに表示要素を処理する foreach $entry (@entries){ # 表示要素追加時チェック部(5) ($published,$link,$title,$econtent,$name,$modified,$created, $id,$updated,$summary,$category) = split(/\@\@\@/,$entry); print "<tr bgcolor=\"#eeeeee\">"; # タイトルとリンクの処理 if($title){ $title = &entities_decode($title); # タイトルにリンクがある場合は、別にリンクする if($title =~ s/(<a.+?)>(.+?)(<\/a>)/$2/i){ $titlelink = " -> $1 target=\"main\">LINK$3"; }else{ $titlelink = ""; } # ATOM の entry 主要素の link 要素の場合 if($link){ print "<td><font color=\"#000000\"><a href=\"", $link, "\" target=\"main\">", $title, "</a></font>$titlelink</td>"; }else{ print "<td><font color=\"#000000\">$title</font>$titlelink</td>"; } }else{ # タイトルがない場合は、リンク文字列にリンクしてタイトルに表示する # ATOM の entry 主要素の link 要素の場合 if($link){ if($link !~ /http:/){ # PATH のみの URL の場合、 ($linkhead = $feed{'link'}) =~ s/^(http:\/\/.+?\/) (~\w+?\/)*.*$/$1$2/; # feed のリンクから BASE URL を取り出し、 $link = $linkhead . $link; # PATH に URL を付加する。 } print "<td><font color=\"#000000\"><a href=\"", $link, "\" target=\"main\">", $link, "</a></font></td>"; }else{ print "<td><br></td>\n"; } } # entry 記事相当要素の出力 if($econtent){ if(($econtent =~ /(&[a-z0-9]{2};|&#\d+;)/ && $preexist == 0) or ($cdexist == 1) or ($mode eq 'escaped')){ $econtent = &entities_decode($econtent); # 実体参照への変換が二重に行われている場合の対応 if($econtent =~ /(&[a-z0-9]{2};|&#\d+;)/){ $econtent = &entities_decode($econtent); } } # HTML 表の表示が乱れるため、<p>タグは除去、 # </p>タグを<br>に置換して出力 $econtent =~ s/<p[^r>]*>//ig; $econtent =~ s/<\/p>/<br \/>/ig; print "<td>",$econtent,"</td>\n";$econtent = ""; }elsif($summary){ if(($summary =~ /(&[a-z0-9]{2};|&#\d+;)/ && $preexist == 0) or ($cdexist == 1) or ($mode eq 'escaped')){ $summary = &entities_decode($summary); # 実体参照への変換が二重に行われている場合の対応 if($summary =~ /(&[a-z0-9]{2};|&#\d+;)/){ $summary = &entities_decode($summary); } } # HTML 表の表示が乱れるため、<p>タグは除去、</p>タグを<br>に置換して出力 $summary =~ s/<p[^r>]*>//ig; $summary =~ s/<\/p>/<br \/>/ig; print "<td>",$summary,"</td>\n";$summary = ""; }else{ print "<td><br /></td>\n"; } # entry 日付要素 if($published){ print "<td>",$published; if($updated){ print "<br />更新: ",$updated,"</td>\n"; }else{ print "</td>\n"; } }else{ if($updated){ print "<td>更新: ",$updated,"</td>\n"; }else{ print "<td><br /></td>\n"; } } # entry 製作者要素 if($name){ print "<td>",$name,"</td>\n"; }else{ print "<td><br /></td>\n"; } # entry category要素 if($category){ print "<td>",$category,"</td>\n"; }else{ print "<td><br /></td>\n"; } # entry id 要素 # if($id){ # print "<td>",$id,"</td>\n"; # }else{ # print "<td><br /></td>\n"; # } print "</tr>\n"; } print "</table>\n";# Atom ファイルの記事内容表出力終了 print "<hr />\n<ul>\n";# Atom entry副要素リスト出力 foreach (keys (%vals)){ print "<li>",$_,"</li>\n"; } print "</ul></body>\n</html>\n"; # HTML 出力終了 # サブルーチン # マークアップ記号の実体参照デコード # 文字実体参照と数値実体参照の両方に対応 sub entities_decode{ my($str) = @_; $str =~ s/(<|<)/</g; $str =~ s/(>|>)/>/g; $str =~ s/('|')/'/g; $str =~ s/("|")/"/g; $str =~ s/(&|&)/&/g; return $str; } # マークアップ記号の実体参照エンコード sub entities_encode{ my($str) = @_; $str =~ s/&/&/g; $str =~ s/</</g; $str =~ s/>/>/g; $str =~ s/'/'/g; $str =~ s/"/"/g; return $str; }