require 'html-parser'
require 'formatter'
require 'uri'
# 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
@reviewer = reviewer
@review_list = {}
@anchor = nil
@last_anchor_name = nil
end
# というタグ
# があったらその人をデフォルトの書評者とする。
#
def do_meta(attrs)
if !@reviewer
if attrs.assoc("name").to_s.downcase == "author"
@reviewer = attrs.assoc("content")
end
end
end
# コメント部の処理。
# コメント中にISBNがあって、それが...で囲まれていれば
# そのアンカーのリンク先を書評ページとする。
# アンカーになっていない場合はテキスト中のIBSNと同じく、その
# ページ内に書評が書かれているとする。
#
def handle_comment(data)
isbn = lookup_isbn_by_text(data)
if isbn
if @anchor
url = URI.parse(@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
regist_review(isbn)
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)
f = NullFormatter.new()
parser = ReviewDetector.new(f, url)
parser.feed(data)
list = parser.review_list
parser.close()
return list
end
end
end
if __FILE__ == $0
require 'net/http'
Net::HTTP.version_1_2
data = nil
url = URI.parse(ARGV[0])
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])
else
data = File.open(ARGV[0]){|f| f.read()}
list = ReviewDetector.detect(data, "file:///"+ARGV[0])
end
p list
end