require 'html-parser' require 'formatter' require 'uri' # 2003/02/11 # * authorが拾えてなかった(assocの使い方を間違えてた……)ので # 修正。仕様についてはまだ要検討。 # # 2003/02/09 # * detecter -> detector (汗 # * @anchor_name -> @last_anchor_name # * handle_comment を追加。 # RHG # とかいうタグがあった場合、foo.html#t_01をISBN4-8443-1721-0 # の書評と見なすようにする。 # HTML中の書評(ISBN)を自動検出するクラス # class ReviewDetector < HTMLParser # 書評の入れ物クラス # class Review attr_accessor :isbn, :reviewer, :url def initialize(isbn, url, reviewer) @isbn = isbn @url = url @reviewer = reviewer end # ISBNを数値のみにする。識別用に使うためのもの。 # def s_isbn @isbn.gsub(/[^0-9]+/,"") end end attr_reader :review_list # 初期化する。 # 第3引数は書評を書いた人を指定する場合。nilの場合はHTML中の # から検出する。 # def initialize(writer, url, reviewer=nil) super(writer) @url = url @parsed_url = URI.parse(url) @reviewer = reviewer @review_list = {} @anchor = nil @last_anchor_name = nil end # というタグ # があったらその人をデフォルトの書評者とする。 # def do_meta(attrs) if !@reviewer if attrs.assoc("name") && attrs.assoc("name")[1].to_s.downcase == "author" reviewer_list = attrs.assoc("content") if reviewer_list @reviewer = reviewer_list[1] end end end end # コメント部の処理。 # コメント中にISBNがあって、それが...で囲まれていれば # そのアンカーのリンク先を書評ページとする。 # アンカーになっていない場合はテキスト中のIBSNと同じく、その # ページ内に書評が書かれているとする。 # def handle_comment(data) isbn = lookup_isbn_by_text(data) if isbn if @anchor url = @parsed_url.merge(URI.parse(@anchor)).to_s regist_review(isbn, url) else regist_review(isbn) end end end # の処理 # def anchor_bgn(href, name, type) isbn = lookup_isbn_by_href(href) if isbn regist_review(isbn) end @anchor = href if name @last_anchor_name = name end end # の処理 # def anchor_end() @anchor = nil end # HTML内のテキスト部分の処理。 # テキスト中にISBNがあって、それが...で囲まれていれば # そのアンカーのリンク先を書評ページとする。 # アンカーになっていない場合は、そのページに書評が # 書かれているとして、直前のに書かれているname # 属性のところを書評として登録する。 # その場合、name属性が指定してあるAタグがないときにはページ # そのものを登録する。 # def handle_data(data) isbn = lookup_isbn_by_text(data) if isbn if @anchor url = @parsed_url.merge(URI.parse(@anchor)).to_s regist_review(isbn, url) else regist_review(isbn) end end end # 書評をリストに格納する # def regist_review(isbn, url=nil) if url review = Review.new(isbn, url, @reviewer) else if @last_anchor_name review = Review.new(isbn, @url+"#"+@last_anchor_name, @reviewer) else review = Review.new(isbn, @url, @reviewer) end end @review_list[review.s_isbn] = review end # href属性値の中からISBNを探す # def lookup_isbn_by_href(href) if %r|www.amazon.co.jp/exec/obidos/ASIN/([0-9][-0-9X]+)/|i =~ href return $1 elsif %r|www.bk1.co.jp/[^'"]*isbn=([0-9][-0-9X]+)|i =~ href return $1 end return nil end # 文字列の中からISBNを探す # def lookup_isbn_by_text(data) if %r|isbn[\s=:]?([0-9][-0-9X]+)|i =~ data return $1 end end ## for simple use class << self def detect(data, url, reviewer=nil) f = NullFormatter.new() parser = ReviewDetector.new(f, url, reviewer) parser.feed(data) list = parser.review_list parser.close() return list end end end if __FILE__ == $0 # コマンドラインでの使い方 # URLは必須。reviewerの指定はオプションで、指定しない # 場合は自動検出を行う。 # def usage() print "ruby reviewdetector.rb []\n" exit 1 end require 'net/http' Net::HTTP.version_1_2 usage() if ARGV.size < 1 or ARGV.size > 2 data = nil url = URI.parse(ARGV[0]) if ARGV[1] reviewer = ARGV[1] else reviewer = nil end if url.scheme == "http" http = Net::HTTP.new(url.host, url.port) http.start{ res, = http.get(url.path) data = res.body } list = ReviewDetector.detect(data, ARGV[0], reviewer) else data = File.open(ARGV[0]){|f| f.read()} list = ReviewDetector.detect(data, "file:///"+ARGV[0], reviewer) end # ISBN、URL、書評者をtab区切りで出力 # list.each{|key, item| printf("%s\t%s\t%s\n", item.isbn, item.url, item.reviewer) } end