風柳メモ

ソフトウェア・プログラミング関連の覚書が中心。

ふたつの記事を共にブックマークしているユーザの抽出

元ネタ

「情報の取得」と「情報の比較」部分はRubyにやってもらえたのですが、その間の「加工」部分は秀丸エディタと正規表現の力を借りて、手作業で

http://d.hatena.ne.jp/hrkt0115311/20090723/1248341463

せっかくなんで、もうひと押し。

(doc/"li/a.username").each do |id|
    user_name << id
end

(doc/'ul[@id="bookmarked_user"]/li/a[@class="username"]').each do |id|
    user_name << id.inner_html
end

にすれば、ユーザ名だけ取り出せるので、加工の必要がなくなるはず。

やってみる

hb_user_compare.rb
require 'rubygems'
require 'hpricot'
require 'open-uri'

# デバッグ用
DEBUG = false # true でデバッグモード

# 複数ページ取得処理:ページ読込み処理を平行して行い、待ち時間を短縮
def multi_fetch(url_list)
  threads,docs = {},{}
  url_list.uniq.each do |url|
    # ページ毎にスレッド作成
    threads[url] = Thread.new(url) do
      puts "[Thread] Start: #{url}" if DEBUG
      begin
        docs[url] = Hpricot( open(url).read ) # ページを読み込んでドキュメント化
        puts "[Thread] End  : #{url}" if DEBUG
      rescue NameError => err
        # スレッド中で open() すると時々失敗してしまうので(NameError)リトライ処理を入れておく
        # 【正しい対応方法、どなたか教えて下さい】
        puts "Error : #{err} (#{url})" if DEBUG
        Thread.pass
        retry
      end
    end
  end
  url_list.each do |url|
    puts "Waiting: #{url}" if DEBUG
    threads[url].join
    puts "Done." if DEBUG
  end
  return docs
end

# コマンドライン引数よりブックマークエントリページURL取得
url_list = []
ARGV.each do |url|
  if %r{^http://b\.hatena\.ne\.jp/entry/.+$} =~ url
    burl = url
  elsif %r{^http://} =~ url
    burl = 'http://b.hatena.ne.jp/entry/' + url.sub(%r{^http://}, "").gsub(/#/, "%23")
  elsif %r{^https://} =~ url
    burl = 'http://b.hatena.ne.jp/entry/s/' + url.sub(%r{^https://}, "").gsub(/#/, "%23")
  else
    burl = 'http://b.hatena.ne.jp/entry/' + url.gsub(/#/, "%23")
  end
  url_list << burl if burl
  puts "URL(bookmark): #{burl}" if DEBUG
end
  
# ページ取得&ユーザ抽出
docs = multi_fetch(url_list) # 複数ページ取得

def print_users(user_list,header)
  puts "*** #{header}"
  if user_list.size<2
    puts "#{user_list.size} user"
  else
    puts "#{user_list.size} users"
  end
  puts user_list.sort{|a,b|a.upcase()<=>b.upcase()}.join(', ')
  puts ''
end

count,user_or_list,user_and_list = 0,[],[]
url_list.each do |url|
  user_list = []
  # ■XPathを用いて抽出
  (docs[url]/'ul[@id="bookmarked_user"]/li/a[@class="username"]').each do |link|
    user_list << link.inner_html.gsub(/\s/,'')
  end
  print_users(user_list,url)
  if count == 0
    user_and_list = user_or_list = user_list
  else
    user_and_list = user_and_list & user_list
    user_or_list = user_or_list | user_list
  end
  count+=1
end

if 1 < url_list.size
  print_users(user_or_list,'user-list(or)')
  print_users(user_and_list,'user-list(and)')
end

コマンドラインから

hb_user_compare.rb http://d.hatena.ne.jp/hrkt0115311/20090331/1238448821 http://d.hatena.ne.jp/hrkt0115311/20090720/1248041988

のようにやれば、

  • 各URL毎のブックマークユーザリスト
  • いずれかをブックマークしたユーザリスト(or)
  • いずれもブックマークしたユーザリスト(and)

を表示します。
いちいち頭にhttp://b.hatena.ne.jp/entry/を付けるのは面倒なので、自動付加(なお、付けてあっても判別してそれなりに動くはず)。あと、3つ以上指定もできる、かも。