ローイングファンのプログラミング日記

ボート競技やプログラミングについて書きます

ローイングファン version1

今まで試してきたコードを使ってボートレース(レガッタ)の情報を検索するアプリをつくる。
前につくったお試し版に改良をくわえ、一通りの機能は備わったのでversion1とする。

目次


ローイングファンver1のコード

コードを実行すると案内が表示されます。
案内にしたがってURLとボートクラブ名を入力するとターミナル/PowerShellに結果が表示され、エクセルで使えるCSVファイルが作成されます。
コードの説明は今までの記事を読んでください。

rowingfan_ver1.rb

#-------------------------------------
# コードを実行するとクローリングが実行されます。
# URL先の注意事項を読んでから使用してください。
# https://rowingfan.hatenablog.jp/entry/2018/03/11/125951
# 対応しているボートレース(レガッタ)は次の4大会です。
# 全日本選手権、全日本軽量級選手権、全日本大学選手権、全日本新人選手権
# 実行するとターミナル/PowerShellに結果が表示されます。
# 実行するとCSVファイルが作られます(encoding: Shift_JIS)
#-------------------------------------
require 'open-uri'

puts "\n検索したい種目のURLを入力して下さい。"
print "URL: " 
url_entered = gets.chomp!.encode("utf-8")
sleep(0.5)

if url_entered == ""
    puts "\n\n<!> URLが入力されていません"
    sleep(0.5)
    puts "入力しなおす: 1\n終了する: 2"
    print "番号(半角)を入力: " 
    choice_reurl = gets.to_i
    if choice_reurl == 1
        puts "\n検索したいレースのURLを入力して下さい。"
        print "URL: "
        url_entered = gets.chomp!.encode("utf-8")
        sleep(0.5)
    else
        exit
    end
end
#URLチェック
if /\Ahttps:\/\/www.jara.or.jp\/.+\d\d\d\d.+.html\z/ =~ url_entered
    url = url_entered
else
    print "\n\nレース情報が見つからないので終了します...\n\n"
    exit
end

#-----URLをクローリング-----
#例外処理つき
begin
    #open-uriで開くデータはバイト列なのでr:utf-8をつける
    html = open("#{url}","r:utf-8")
    htmlcode = html.read
    html.close
    sleep(1)
rescue
    print "\n\nURLが見つからないので終了します...\n\n"
    exit
end
#-----年代、大会名、種目名をスクレイピング-----
#大会名をタグ付きキャプチャで取得
/<h1 class=\"title\">第\d+(?<tag_tourname>.+)大会<\/h1>/ =~ htmlcode
#年代をタグ付きキャプチャで取得
/<a href=\"(?<tag_year>\d\d\d\d).+><i class=\"glyphicon glyphicon-file\"><\/i>/ =~ htmlcode
#種目名をタグ付きキャプチャで取得
/<div class=\"panel-heading\">(?<tag_event>.+)の組合せと結果<\/div>\n/ =~ htmlcode
# //---年代、大会名、種目名をスクレイピング---

if tag_tourname && tag_year && tag_event
    puts "\n\n次のレースから検索します >>"
    puts "=========="    
    puts "#{tag_year} #{tag_tourname}"
    puts "#{tag_event}"
    puts "=========="
    sleep(0.5)
else
    print "\n\nURLがみつかりませんでした。\n\n"  
    exit
end

puts "\n検索したいボートクラブ名を入力して下さい。"
puts "(AやBなどの符号は不要です)"
print "クラブ名: "
clubname = gets.chomp!.encode("utf-8")
if clubname == ""
    puts "\n\n<!> クラブ名が入力されていません <!>"
    sleep(0.5)
    puts "入力しなおす: 1\n終了する: 2"
    print "番号(半角)を入力: " 
    choice_reclubname = gets.to_i
    if choice_reclubname == 1
        puts "\n検索したいボートクラブ名を入力して下さい。"
        print "クラブ名: "
        clubname = gets.chomp!.encode("utf-8")
        sleep(0.5)
    else
        exit
    end
end
sleep(0.5)
puts "\n\n次のレース情報を検索します >>"
sleep(0.5)
puts "==============="
puts "大会: #{tag_year} #{tag_tourname}"
puts "種目: #{tag_event}"
puts "団体: #{clubname}"
puts "==============="
print "\n\n"
puts "よければ 1\n中止する場合は 2"
print "番号(半角)を入力: "
choice_serch = gets.to_i
puts "\n"
if choice_serch == 1
    #---レース情報をスクレイピング---
    #シングルスカル種目のクルー名にある余計なタブを削除
    htmlcode.gsub!(/<br.?\/><small>/,'')
    htmlcode.gsub!(/<\/small>/,'')
    #レース記号をタグ付きキャプチャ
    /\Ahttps:\/\/www.+\/(?<tag_urlyear>\d\d\d\d)(?<tag_urltour>.+)_(?<tag_urlevent>.+).html\z/ =~ url
    #Race Noを取得
    acquired_racenos = htmlcode.scan(/Race No:\s(?<tag_receno>\d+)<\/div>/)
    #発艇日時を取得
    acquired_starttimes = htmlcode.scan(/発艇時刻:<\/b>\s(?<tag_start>\d+\/\d+\s\d+:\d+)<\/div>/)
    #組別を取得
    acquired_groups = htmlcode.scan(/<b>組別:<\/b> <a href=\"\d+.+_tt.html.day\d+_\d+_\d+\">(?<tag_group>.+)<\/a><\/div>/)
    #順位を取得
    acquired_ranks = htmlcode.scan(/<tr>\n.*<td class=\"text-right\">(?<tag_rank>\d?)<\/td>/)
    #クルー名を取得
    acquired_crews = htmlcode.scan(/<td class=\"crew\">(?<tag_crew>.*)<\/td>/)
    #500mタイムを取得
    acquired_lap1qs = htmlcode.scan(/<td class=\"crew\">.*<\/td>\n.*<td class=\"text-center\">(?<tag_q1>\d*:?\d*.?\d*)<\/td>\n/)
    #1000mタイムを取得
    acquired_lap2qs = htmlcode.scan(/<td class=\"crew\">.*<\/td>\n.+<\/td>\n.*<td class=\"text-center\">(?<tag_q2>\d*:?\d*.?\d*)<\/td>\n/)
    #1500mタイムを取得
    acquired_lap3qs = htmlcode.scan(/<td class=\"text-center\">(?<tag_q3>\d*:?\d*.?\d*)<\/td>\n.*<td class=\"text-center\">.*<\/td>\n.*<td class=\"text-right\">\d*<\/td>\n/)
    #2000mタイムを取得
    acquired_lap4qs = htmlcode.scan(/<td class=\"text-center\">(?<tag_q4>\d*:?\d*.?\d*)<\/td>\n.*<td class=\"text-right\">\d*<\/td>\n/)
    #レーンNoを取得
    acquired_lanenos = htmlcode.scan(/<td class=\"text-right\">(?<tag_lane>\d?)<\/td>\n.*<td class=\"qualify\">.*<\/td>\n/)
    #備考を取得
    acquired_qualifys = htmlcode.scan(/<td class=\"qualify\">(?<tag_qualify>.*)<\/td>\n/)
    #//---レース情報をスクレイピング---

    #順位別情報の行と列を入れ替える
    infos_by_rank = acquired_ranks.zip(acquired_crews,acquired_lap1qs,acquired_lap2qs,acquired_lap3qs,acquired_lap4qs,acquired_lanenos,acquired_qualifys)
    #id情報(レースNo,日時など)の行と列を入れ替える
    identifiers = acquired_racenos.zip(acquired_starttimes,acquired_groups)
    #id情報に種目名を格納する
    identifiers.each do |identifier|
        identifier.insert(1,tag_event)
    end

    #全レース結果が入る配列を作成
    raceinfos_all = []
    #1レース単位の結果が入る配列を作成
    raceinfo_by_race = []
    count = 0
    infos_by_rank.each do |infobyrank|
        count += 1
        infobyrank.flatten!
        #剰余を使った条件判断(1着から6着で区切った次で配列を初期化する)
        raceinfo_by_race = [] if count % 6 == 1
        raceinfo_by_race << infobyrank
        #1着から6着を1セットとして全レース結果の配列に格納する
        raceinfos_all << raceinfo_by_race if count % 6 == 0
    end

    #id情報(RaceNo、日時など)と順位別レース結果の行と列を入れ替える
    all_in_raceinfos = identifiers.zip(raceinfos_all)
    #不要な配列を平坦化
    all_in_raceinfos.each do |info_by_race|
        info_by_race[0].flatten!
    end

    puts "----------"
    puts "#{tag_year}#{tag_tourname}"
    puts "----------"
    sleep(1.5)
    count_serch = 0
    all_in_raceinfos.each do |info_by_race|
        if /.*#{clubname}[A-Z]?\)?\z/i =~ info_by_race[1][0][1] ||  
            /.*#{clubname}[A-Z]?\)?\z/i =~ info_by_race[1][1][1] ||  
            /.*#{clubname}[A-Z]?\)?\z/i =~ info_by_race[1][2][1] ||  
            /.*#{clubname}[A-Z]?\)?\z/i =~ info_by_race[1][3][1] ||  
            /.*#{clubname}[A-Z]?\)?\z/i =~ info_by_race[1][4][1] ||  
            /.*#{clubname}[A-Z]?\)?\z/i =~ info_by_race[1][5][1]
            count_serch += 1
            #---ターミナル/PowerShellに出力---
            #id情報の出力
            puts "RaceNo #{info_by_race[0][0]} #{info_by_race[0][1]} #{info_by_race[0][3]}\n#{info_by_race[0][2]}"
            #順位別情報の出力
            info_by_race[1].each do |rankinginfos|
                puts "#{rankinginfos[0]}#{rankinginfos[1]} #{rankinginfos[5]} #{rankinginfos[7]}" if rankinginfos[1] != ""
            end
            puts "---------------"
            #---CSVファイル作成---
            File.open("#{tag_urlyear}_#{tag_urltour}_#{tag_urlevent}.csv","a:shift_jis") do |csv|
                csv.write "#{info_by_race[0][0]}\n#{info_by_race[0][1]} #{info_by_race[0][3]}\n#{info_by_race[0][2]}\n"
                csv.write "rank,crew,500m,1000m,1500m,2000m,lane,remarks\n"
                info_by_race[1].each do |rankinginfos|
                    csv.write "#{rankinginfos[0]},#{rankinginfos[1]},#{rankinginfos[2]},#{rankinginfos[3]},#{rankinginfos[4]},#{rankinginfos[5]},#{rankinginfos[6]},#{rankinginfos[7]}, #{rankinginfos[8]}\n"
                end
                csv.write "\n"
            end
        end #// if /.*#{clubname}[A-Z]?\)?\z/i
    end #// all_in_raceinfos.each

    if count_serch == 0
        print "クラブが見つかりませんでした。\n" 
    else
        print "\n合計#{count_serch}レース見つかりました。\n"
    end
end #//.if choice_serch == 1


更新履歴

ローイングファンの更新履歴。

ver1.1
手続き型のコードをオブジェクト指向型のコードに書きかえる - ローイングファンの日記


Webアプリ ローイングファンのURL
https://rowingfan.herokuapp.com/

今回の動作確認環境

macOS 10.13.4
Ruby 2.4.3p205
ターミナル 2.8.2

Windows10 1709
Ruby 2.4.4p296
Windows PowerShell 5.1