論文リストのデータ形式

論文のサーベイや発表論文の情報を掲載するにあたり ymatsuo.com で用いられているデータ形式を用いている.その形式は以下のようなものである.

<div class="paper"> <div class="title">論文名</div> <span class="author"><u>自分</u>, 共著者1, 共著者2,.... </span>, <span class="publication">会議 or 雑誌名</span>, <span class="year">西暦</span>, <span class="pages">ページ</span> <span class="authorinfo">著者情報</span> <div class="description">論文概要</div> <div class="comment">論文に対するコメントなど</div> <div class="keyword">KW: キーワード</div> </div>

上記に用いられているタグはクラス名が同じであればタグ名は異っても問題ない(と思う).ただし,論文データを構成する "author", "title" などは,"paper" をクラス名にもつタグの子ノードである必要がある.

RubyによるRSSデータの出力

プログラムの作成方針としては,HTMLファイル内にある "paper" をクラス名にもつタグを探し,その子ノードを論文の情報を構成する要素であることを前提としている.

新しいサーベイ情報を追加するなど,RSSデータを更新する場合は,既存のRSSデータ(XMLファイル)とHTMLファイル内の論文情報とを比較して,追加もしくは更新されたデータの変更を行う.ただし,RSSの要素の粒度は,上記の論文データ形式に比べて荒いために,RSSの要素のタイトル情報を,title+publication, サマリー情報で,author+publication+descriptionを保存し,それぞれが異れば更新or追加されたと見なしている.これは,国際会議と雑誌とで同タイトルで公開されている場合などを想定した措置である.

HTMLファイルの読み込み

HTMLファイルを読み込むにあたり,まず問題になるのは文字コードの問題がある.ファイルもメタタグを信じる.文字コードを判別するなどいろんな方法を試したが,あらゆるケースに追いて確実に対応できる方法が密からかなったため,多少強引ではあるが,nkf を使ってファイルの文字コード強引に変えるという方法をとった.

  # 入力ファイルを nkf を使って文字コードを utf-8 に変換
  inputFilename = ARGV.shift                 # 元データ HTMLファイル名
  day = Time.now
  datestring = day.strftime("%y%m%d-%H%M%S") # tmpファイル用 datetime を取得
  tmpfilename = "tmp_SPRSS#{datestring}.txt" 
  tmpfile = open(tmpfilename,"w")
  File.open(inputFilename) { |file|
    while line = file.gets
      tmpfile.print NKF.nkf("-sjis",line)    # エンコードを sjis に変換
    end
  }
  tmpfile.close 

直接 UTF8に変換しないで,SJISに変換しているのは文字化けが起きる場合があるのでSJISにしてある.EUCでも良かった気もするが確証はない.

当然,作成した tmp ファイルは,処理の一番最後で削除する.

 File.delete(tmpfilename) # tmp ファイル  

HTMLファイルの処理

HTMLファイルを処理するにあたり,hypricot を利用している.これは,ruby が動作する環境にて,rubygems を用いてインストールしてある必要がある.なお,そのプロセスの説明は,検索エンジンで探せばみつかるので,ここでは省く.

先に SJIS に変換したtmpファイルを読み込み hpricot を使ってデータの処理を行う.

  htmldata = Hpricot(File.read(tmpfile))
  (htmldata/".paper").each do |p|     # class名が "paper" であるタグのリストを作成
    paper = Hash.new                  # 論文データの抽出
    p_title = "\"#{p.at("[@class='title']").inner_text.sub(/\[\d+?\]/,"").sub(/^\s+/,"").sub(/\s+$/,"")}\""
    p_title += ":\t#{p.at("[@class='publication']").inner_text}"
    p_desc  = "Author: #{p.at("[@class='author']").inner_text}
" p_desc += "Publication: #{p.at("[@class='publication']").inner_text}
" p_desc += p.at("[@class='description']").inner_text tag = p.at("[@class='link']") p_link = nil tag.search("a") { |ele| p_link = ele['href'] } unless tag == nil if $ifencode == 'EUC' # 文字コード処理の名残り p_title = Uconv.euctou8(p_title) p_desc = Uconv.euctou8(p_desc) p_link = Uconv.euctou8(p_link) elsif $ifencode == 'SJIS' p_title = Uconv.sjistou8(p_title) p_desc = Uconv.sjistou8(p_desc) p_link = Uconv.sjistou8(p_link) end paper['title'] = p_title paper['desc'] = p_desc paper['link'] = p_link paper['date'] = fstat.mtime paperList.push(paper) # 論文データの保存
(2008.02.05) 修正: p_link の値の有効な範囲が間違っていたため RSS を作成する時点では値が nil とされてしまっていたため出力されていなかったのを修正

RSSデータの出力

RSSの出力は,ruby の標準ライブラリに入っているので ruby が動作する環境であれば問題なく利用できるはず.

def mkRSS(rssuri,name,description,link, paperList)
  rss = RSS::Maker.make("2.0") do |maker|
    maker.channel.title = name
    maker.channel.description = description
    maker.channel.link  = link

    maker.items.do_sort = true
    maker.encoding = "UTF-8"      # RSSの文字コード
    
    paperList.each do |paper|     # 論文データの登録
      maker.items.new_item do |item|
        item.title = paper['title']
        item.link = paper['link'] unless paper['link'] == nil
        item.description = paper['desc']
        item.date = paper['date']
      end
    end
  end
end

登録したRSSのデータは .to_s で文字に変換できるので,それをファイルなりに出力すればよい.

データの更新

既存のRSSファイルを更新するには,既存のファイルを読み込み,そこにあるデータと元データとの比較が必要である.よって,rss ライブラリでは,データの読み込みにも対応しているのでそれを利用した.

def readRSS(fname)
  feed = nil
  begin
    feed = RSS::Parser.parse(File.read(fname),false)
  rescue RSS::Error
    STDERR.puts "#{fname}はRSS 0.9x/1.0/2.0, Atom 1.0のいずれでもありません。" if feed == nil
  end
  return feed    
end   

次に読み込んだRSSデータを,一旦文字データとして配列に格納する.この作業をするのは,読み込んだ RSSフィードのデータを上書きしようとしたが,文字データとして出力する時に反映させることができなかったので,お手軽な対処として,もう一度 RSSのフィードを作成することにしたためである.ただし,この時に,RSSの各アイテム部分の更新時間の情報を,追加・更新されたアイテムに関しては実行した時間,それ以外は,既存のデータの時間にしてある.

  papers.each do |paper|
    flagp = false
    rss.items.each do |item|
      if item.title == paper['title']
        if item.description != paper['desc']
          item.description = paper['desc']
          item.date = paper['date']
        end
        flagp = true
      end
      if item.description == paper['desc']
        if item.title != paper['title']
          item.title = paper['title']
          item.date = paper['date']
        end
        flagp = true
      end
    end
    next if flagp 
    item = RSS::RDF::Item.new
    item.title = paper['title']
    item.link = paper['link'] unless paper['link'] == nil
    item.description = paper['desc']
    item.date = paper['date']
    rss.items << item    
  end
  return rss 

プログラム

論文リストのRSS出力プログラム

- SPRSS.rb

実行方法

- 新規のRSSの出力の場合

 % ./SPRSS.rb [元HTMLファイル名] "RSSタイトル" "RSS説明" "RSSタイトルにリンクするURL" > [出力ファイル名] 

新規のRSSを出力したあとは,元データとなる HTMLファイルにメタ情報を追加する必要がある.

- 既存のRSSの更新の場合

 % ./SPRSS.rb -update [元HTMLファイル名] [既存RSSファイル名]