$KCODE = 'u' require 'rubygems' require 'hpricot' require 'open-uri' require 'nkf' require 'uri' require 'timeout' require 'yaml' require 'optparse' require 'net/http' class Starbucks attr_accessor :storeId attr_accessor :name attr_accessor :address attr_accessor :lat attr_accessor :lng attr_accessor :tel attr_accessor :open_at def initialize(id, name, lat, lng, addr, tel, op) @storeId = id @name = name @lat = lat @lng = lng @address = addr @tel = tel @open_at = op end def open_eng @open_at.gsub(/\s/, "") \ .gsub("定休日", "Reg.Holiday") \ .gsub("不定休", "unfixed") \ .gsub(/(曜日)|曜/, "") \ .gsub(/(祝日)|祝/, "Holiday") \ .gsub("〜", "-") \ .gsub("・", "") \ .gsub("は", ": ") \ .gsub("ドライブスルー", "DriveThru ") \ .gsub("/", "/ ") \ .gsub("月", "Mon.") \ .gsub("火", "Tue.") \ .gsub("水", "Wed.") \ .gsub("木", "Thu.") \ .gsub("金", "Fri.") \ .gsub("土", "Sat.") \ .gsub("日", "Sun.") end def open_jpn @open_at.gsub(/\s/, "") end def to_s "%d,%8.5f,%8.5f,%s,%s" % [@storeId, @lng, @lat, @name, @address] end def to_poi(lang) case lang when :japanese "%8.5f,%8.5f,\"Starbucks %s\", \"%s\"" % [@lng, @lat, @name, open_jpn()] else "%8.5f,%8.5f,\"Starbucks\", \"%s\"" % [@lng, @lat, open_eng()] end end def to_kml(lang) case lang when :japanese "\n" + \ "Starbucks #{name}\n" + \ "#{open_jpn()}\n" + \ "#{@lng},#{@lat},0\n" + \ "" + \ "" else "\n" + \ "Starbucks\n" + \ "#{open_eng()}\n" + \ "#{@lng},#{@lat},0\n" + \ "" + \ "" end end end class StarbucksDownloader def initialize(lang, dl, verbose) @lang = lang @dl = dl @verbose = verbose end # Timeout時のretry付きでuriを開く def openURI(uri) retries = 5 begin timeout(30){ Hpricot.parse(NKF.nkf('-w', open(uri).read)) } rescue Timeout::Error retries -= 1 if retries > 0 sleep 5 and retry else raise end end end def download_store_ids # 都道府県ごとにstore idを取得する storeIds = [] prefs = ["北海道","青森県","岩手県","宮城県","秋田県","山形県","福島県","茨城県","栃木県","群馬県","埼玉県","千葉県","東京都","神奈川県","新潟県","富山県","石川県","福井県","山梨県","長野県","岐阜県","静岡県","愛知県","三重県","滋賀県","京都府","大阪府","兵庫県","奈良県","和歌山県","鳥取県","島根県","岡山県","広島県","山口県","徳島県","香川県","愛媛県","高知県","福岡県","佐賀県","長崎県","熊本県","大分県","宮崎県","鹿児島県","沖縄県"] prefs.each do |pref| storeIds.concat download_store_ids_by_prefecture(pref) end storeIds end def download_store_ids_by_prefecture(pref) storeIds = [] uri = URI.escape("http://www.starbucks.co.jp/search/result_store.php?SearchString=#{NKF.nkf('-s', pref)}") while uri != nil do # 「次の10件」がなくなるまでidの読み取りを繰り返す ids, uri = download_store_ids_in_uri(uri) storeIds.concat ids end puts "Number of Starbucks in #{pref} : #{storeIds.size}" storeIds end # store IDの取得 def download_store_ids_in_uri(uri) storeIds = [] nxt_uri = nil doc = openURI(uri) (doc/:a).each do |a| nxt_uri = "http://www.starbucks.co.jp/search/result_store.php" + a[:href] if a.inner_text =~ /次の10件/ if a[:href] =~ /storeId=\d+/ then storeIds << a[:href].scan(/storeId=(\d+)/).flatten[0].to_i end end [storeIds, nxt_uri] end # 改行や?などの記号を除去 def strip(str) str.gsub!("?", "") str.gsub("\n", "/") end # 日本測地系(秒単位)から世界測地系へ変換 def conv(ln, la) # 経度、緯度 (単位:度) lng = ln - la * 0.000046038 - ln * 0.000083043 + 0.010040; lat = la - la * 0.00010695 + ln * 0.000017464 + 0.0046017; [lng, lat] end # 店舗情報の取得 def download_store(id) uri = "http://www.starbucks.co.jp/search/map/result.php?storeId=#{id}&lang=ja" doc = openURI(uri) html = doc.to_original_html lng = html.scan(/reqX\s*=\s*(\d+\.\d+)/).flatten[0].to_f # 経度(reqX) lat = html.scan(/reqY\s*=\s*(\d+\.\d+)/).flatten[0].to_f # 緯度(reqY) lng /= 3600.0 # 秒->度 lat /= 3600.0 lng, lat = conv(lng, lat) name = strip(doc.at("th/[text()*='店舗名']").parent.next_sibling.to_plain_text) addr = strip(doc.at("th/[text()*='住所']").parent.next_sibling.to_plain_text) tel = strip(doc.at("th/[text()*='電話番号']").parent.next_sibling.to_plain_text) op = strip(doc.at("th/[text()*='営業時間']").parent.next_sibling.to_plain_text) Starbucks.new(id, name, lat, lng, addr, tel, op) end def download_stores storeIds = download_store_ids stores = [] storeIds.each do |id| begin s = download_store(id) stores << s if s != nil rescue => e puts "Error in getting store #{id}. Skip..." end puts s.to_s if @verbose end stores end def get_stores if Dir::glob("starbucks.yaml").size > 0 && !@dl then YAML.load_file("starbucks.yaml") else stores = download_stores dump_yaml(stores) stores end end def dump_yaml(stores) YAML.dump(stores, File.open('starbucks.yaml', 'w')) end # POIファイルを出力 def dump_poi stores = get_stores open("starbucks_poi.csv", 'w') do |f| stores.each do |s| f.puts NKF.nkf('-s', s.to_poi(@lang)) end end end def dump_kml stores = get_stores open("starbucks.kml", 'w') do |f| f.puts "\n" + \ "\n" + \ "" stores.each do |s| f.puts s.to_kml(@lang) end f.puts "\n" end end end lang = :english dl = false verbose = false kml = false OptionParser.new {|opt| opt.on('-v', 'verbose mode') {verbose = true} opt.on('-j', 'output japanese') {lang = :japanese} opt.on('-f', 'download info. instead of existing yaml file') {dl = true} opt.on('-k', 'output kml') {kml = true} }.parse!(ARGV) sd = StarbucksDownloader.new(lang, dl, verbose) unless kml then sd.dump_poi else sd.dump_kml end