いよいよ今月末にF1が開幕します! ということで、この企画(?)もちゃんと形になるようにスパートをかけていかなければなりません。とはいえ、結局のところは自己満足でしかないんですけどねw
今のところ
- ドライバーズポイントランキングの取得、ページ生成(2016年版の改良)
- レースごとのリザルトページのURL取得、CSV出力
- リザルトページから着順などのデータを取得、簡易ページ生成、CSV出力
といったことをやってきました。ここから、ドライバーズポイントのCSVなどを作成していこうかと思っていたんですが、少し思考を変えて
【全戦の結果をCSVで集計する】
にしてみたいと思います。これによって
- 各レースでの簡易的な結果
- ドライバーごとの順位変動
- ドライバーのポイント順位
なとが取り出せるファイルになるはずです。もちろん、全戦の結果を集計するといっても基本的には
*リザルトページから取得したデータを用いる
という部分に変更はないので、あとはどういう風にデータを処理していくかを工夫していきたいと思います。
CSVデータのイメージを考えてみる
先ほど出した項目を考慮してザックリとイメージしてみます。
ドライバー単位でレースの結果データからポイントとなるものをCSVに組み込んでいく形。内容は
- 順位
- ポイント
- ラップ(タイム差)
の3項目としました。これによって簡易的な結果表示はこのCSVを確認すれば取り出せる仕様になるはずです。もちろんレース毎の詳細なデータは以前出力するようにしたCSVで保存しておけば問題ないですしね。
2016年の全戦をデータ化してみる
前回のプログラムを利用していきます。部分部分を取り出して書いていきますので、全コードを確認したい場合はページの一番下をみてみてください。
ダウンロード実行部分をサブルーチン化
まず、ダウンロード実行部分は何度か利用する場面がでてくると思われるので、これをサブルーチン化してしまいます。といっても、作業は実行部分をプログラムの最後に持っていって、ラベルとreturnを設定してあげるだけです。
/* ダウンロードサブルーチン化 */ * dl_start // URL分解 if (instr(download_url, 0, ".html") ! -1) or (instr(download_url, 0, ".php") ! -1) { //.html .phpが含まれているなら split download_url, "/", result url_pagename = result(stat-1) url_address = download_url strrep url_address, url_pagename, "" } else { // 含まれていない場合はindex.htmlにする url_address = download_url url_pagename = "index.html" } neturl url_address netrequest url_pagename *main //取得待ち確認 netexec res if res > 0 : goto *comp if res < 0 : goto *bad await 50 goto *main *bad //エラー neterror estr dialog "ERROR "+estr end stop *comp mes "DOWNLOAD 完了" return
また、このルーチンに飛ばす場合は、必ずdownload_urlにURLを入れてあげてから飛ぶ必要があります。また、ダウンロードされたファイル名はurl_pagenameに格納されています。
//レースリザルト一覧ページダウンロード download_url = "https://www.formula1.com/en/results.html/2016/races.html" gosub * dl_start
gpnameやurlを取り出す
CSVで保存されたurlを取り出してダウンドーロする処理を書いていきます。ひとまず、CSVを読み込み直しましょう。
notesel csv_text noteload "Raceresulturl.csv"
読み込んだ内容からレース名とURLを取り出していきます。
repeat notemax noteget text_line, cnt split text_line, ",", gpname(cnt), geturl(cnt) loop
一旦、note系の命令をリセットしておきます。
noteunsel await 1
次に、取り出したURLを使ってダウンロードしていくのですが
- 以前、ダウンロードしてデータ化している場合は除外
- データ化していない場合はデータ化処理をしていく
という2点も追加していきます。
以前、ダウンロードしてデータ化している場合は除外
この判定ですが、
- レースの詳細CSVファイルが存在しているか
を基準にしてみたいと思います。まず、CSVファイルが存在するかどうかの確認。以前、CSVファイルが存在しているかの確認でもつかったdirlist命令を使います。
dirlist chk, gpname+".csv" if (stat ! 0) : (次のレースのチェックに)
これをrepeat~loopの中に入れ込み、そして、データ化していない場合の処理を追加しましょう。
データ化処理をサブルーチン化する
まず、先述の部分をコードにしておきます。
repeat 40, 1 if gpname(cnt) = "" : break //レース名が空なら終了 //レース結果のCSVファイルが存在してるかチェック dirlist chk, gpname(cnt)+".csv" if (stat ! 0) : continue //存在していない場合、ダウンロードする download_url = geturl(cnt) gosub *dl_start //データ化処理をする gpname_now = gpname(cnt) gosub *race_csv loop
ここでデータ化処理をするという部分に注目してください。gpname_nowという入れ物を用意して現在のgpname(cnt)の中身を入れています。これはサブルーチン先で使うための処理となっています。そして、race_csvというラベル先でデータ化処理をしていきます。
処理については以前作成したものを利用します。先程のサブルーチン化と同じでラベルとreturnを設置し、先程のgpname_nowを使ってファイル名を処理してあげます。
/* レース結果処理サブルーチン化 */ *race_csv notesel htmlfile noteload url_pagename first_cnt = 0 : family_cnt = 0 : country_cnt = 0 : teamname_cnt = 0 position_cnt = 0 : carnumber_cnt = 0 : laps_cnt = 0 : times_cnt = 0 repeat notemax (中略) nkfcnv html_text,html_text,"Sw" //HTML出力 notesel html_text notesave gpname_now + ".html" noteunsel await 1 // csv出力 notesel csv_text notesave gpname_now + ".csv" noteunsel await 1 mes "save html file" return
これでサブルーチン化も完了。実行してみると
ちゃんとデータ化できているようです。さらに、もう一度実行してみても、上書きされずに終了しているのでファイルの存在確認部分もちゃんと機能していますね。
まとめ
コードも含めるとかなり長くなってしまいそうなので、ここで一旦区切ります。次回はこの続きからになります。いつものようにコードを載せておきますので、興味のある方は試してみてくださいね!
//HSPモジュール SAKMISさんのを使用しています //命令→lfcc ファイルネーム //読み込み→改行置換→保存 #module #deffunc lfcc str filename ; mref filename,32 ; mref status,64 exist filename size=strsize if size=-1 : status=-1 : return sdim ss,size+1,1 bload filename,ss,size ii=0 code=0 sdim data,size<<1,1 repeat size tt = peek (ss,cnt) if tt=10 : code=10 : break if tt=13 { code=13 tt = peek (ss,cnt+1) if tt=10 : code=0 break } loop if code=0 : status=-1 : return repeat size tt = peek (ss,cnt) if tt=code : wpoke data,ii,2573 : ii+2 : continue poke data,ii,tt : ii++ loop bsave filename,data,ii status=ii return #global #include "hspinet.as" // csv_check dirlist chk, "Raceresulturl.csv" await 1 if (stat = 0) { csv_text = "GP Name,URL\n" } else { notesel csv_text noteload "Raceresulturl.csv" noteunsel } // ネット接続の確認 netinit if stat : dialog "ネット接続できません" : end // 初期設定 // download_url = "https://www.formula1.com/en/results.html/2017/drivers.html" // download_url = "https://www.formula1.com/en/results.html/2016/races/938/australia/race-result.html" sdim firstname, 40, 40 sdim familyname, 40, 40 sdim country, 40, 40 sdim teamname, 40, 40 sdim getpoint, 40, 40 // 追加したもの sdim position, 40, 40 sdim carnumber, 40, 40 sdim laps, 40, 40 sdim times, 40, 40 sdim geturl, 200, 40 sdim gpname, 200, 40 //レースリザルト一覧ページダウンロード download_url = "https://www.formula1.com/en/results.html/2016/races.html" gosub *dl_start /*url抽出部分*/ *skippoint //チェック用ラベル notesel htmlfile noteload url_pagename url_cnt = 0 repeat notemax noteget text_line, cnt // RACE_URL if (instr(text_line, 0, "dark bold ArchiveLink") ! -1) { split text_line, "\"", buf // csv_text check if (instr(csv_text, 0, buf(1)) ! -1) : continue // geturl geturl(url_cnt) = buf(1) noteget gpname(url_cnt), cnt+1 strrep gpname(url_cnt), " ", "" gpname(url_cnt) += " GP result" url_cnt++ } loop //test //dialog geturl(0) //dialog gpname(0) noteunsel //make_csv //csv_text = "GP Name,URL\n" repeat 40 if gpname(cnt) = "" : break csv_text += gpname(cnt) + ",https://www.formula1.com" + geturl(cnt) + "\n" loop // csv出力 if (gpname(0) ! "") { notesel csv_text notesave "Raceresulturl.csv" noteunsel await 1 dialog "save new csv file" } // url→raceresultダウンロード notesel csv_text noteload "Raceresulturl.csv" repeat notemax noteget text_line, cnt split text_line, ",", gpname(cnt), geturl(cnt) loop noteunsel await 1 repeat 40, 1 if gpname(cnt) = "" : break //レース名が空なら終了 //レース結果のCSVファイルが存在してるかチェック dirlist chk, gpname(cnt)+".csv" if (stat ! 0) : continue //存在していない場合、ダウンロードする download_url = geturl(cnt) gosub *dl_start //データ化処理をする gpname_now = gpname(cnt) gosub *race_csv loop end stop /* ダウンロードサブルーチン化 */ *dl_start // URL分解 if (instr(download_url, 0, ".html") ! -1) or (instr(download_url, 0, ".php") ! -1) { //.html .phpが含まれているなら split download_url, "/", result url_pagename = result(stat-1) url_address = download_url strrep url_address, url_pagename, "" } else { // 含まれていない場合はindex.htmlにする url_address = download_url url_pagename = "index.html" } neturl url_address netrequest url_pagename *main //取得待ち確認 netexec res if res > 0 : goto *comp if res < 0 : goto *bad await 50 goto *main *bad //エラー neterror estr dialog "ERROR "+estr end stop *comp mes "DOWNLOAD 完了" lfcc url_pagename return /* レース結果処理サブルーチン化 */ *race_csv notesel htmlfile noteload url_pagename first_cnt = 0 : family_cnt = 0 : country_cnt = 0 : teamname_cnt = 0 position_cnt = 0 : carnumber_cnt = 0 : laps_cnt = 0 : times_cnt = 0 repeat notemax noteget text_line, cnt // 着順 if (instr(text_line, 0, "<td class=\"dark\">") ! -1) { strrep text_line, "<td class=\"dark\">", "" strrep text_line, "</td>", "" strrep text_line, " ", "" position(position_cnt) = text_line position_cnt++ continue } // カーナンバー if (instr(text_line, 0, "<td class=\"dark hide-for-mobile\">") ! -1) { strrep text_line, "<td class=\"dark hide-for-mobile\">", "" strrep text_line, "</td>", "" strrep text_line, " ", "" carnumber(carnumber_cnt) = text_line carnumber_cnt++ continue } // ファーストネーム if (instr(text_line, 0, "<span class=\"hide-for-tablet\">") ! -1) { strrep text_line, "<span class=\"hide-for-tablet\">", "" strrep text_line, "</span>", "" strrep text_line, " ", "" firstname(first_cnt) = text_line first_cnt++ continue } // ファミリーネーム if (instr(text_line, 0, "<span class=\"hide-for-mobile\">") ! -1) { strrep text_line, "<span class=\"hide-for-mobile\">", "" strrep text_line, "</span>", "" strrep text_line, " ", "" familyname(family_cnt) = text_line family_cnt++ continue } // 所属チーム if (instr(text_line, 0, "semi-bold uppercase hide-for-tablet") ! -1) { split text_line, ">", buf split buf(1), "<", result teamname(teamname_cnt) = result(0) teamname_cnt++ continue } // ラップ if (instr(text_line, 0, "<td class=\"bold hide-for-mobile\">") ! -1) { strrep text_line, "<td class=\"bold hide-for-mobile\">", "" strrep text_line, "</td>", "" strrep text_line, " ", "" laps(laps_cnt) = text_line laps_cnt++ continue } // タイム if (instr(text_line, 0, "<td class=\"dark bold\">") ! -1) and (instr(text_line, 0, "</td>") ! -1) { split text_line, ">", buf split buf(1), "<", result times(times_cnt) = result(0) times_cnt++ continue } // 獲得ポイント if (instr(text_line, 0, "<td class=\"bold\">") ! -1) { strrep text_line, "<td class=\"bold\">", "" strrep text_line, "</td>", "" strrep text_line, " ", "" getpoint(point_cnt) = text_line point_cnt++ continue } loop noteunsel //make_html html_text = "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta http-equiv=\"content-language\" content=\"ja\">\n<title>2017レースリザルト</title>\n<style>\nbody {\ncounter-reset: drivername;\n}\nli {\ndisplay: flex;\n}\ndiv {\nmargin-right: 1rem;\n}\n.drivername, .team {\nwidth: 12rem;\n}\n.country {\nwidth: 6rem;\n}\n.drivername:before {\ncounter-increment:drivername;\ncontent:counter(drivername) \"位 \";\ndisplay: inline-flex;\nwidth: 3rem;\n}\n</style>\n</head>\n<body>\n<h1>2017レースリザルト</h1>\n<ul>" csv_text = "position,firstname,familyname,carnumber,teamname,point,laps,times\n" repeat 40 if firstname(cnt) = "" : break html_text += "<li>\n<div class=\"position\">[" + position(cnt) + "]</div>\n<div class=\"drivername\">" + firstname(cnt) + " " + familyname(cnt) + "</div>\n<div class=\"country\">" + carnumber(cnt) + "</div>\n<div class=\"team\">" + teamname(cnt) + "</div>\n<div class=\"point\">" + getpoint(cnt) + "pt</div>\n<div class=\"times\">" + laps(cnt) + "(" + times(cnt) + ")</div>\n</li>\n" csv_text += position(cnt) + "," + firstname(cnt) + "," + familyname(cnt) + "," + carnumber(cnt) + "," + teamname(cnt) + "," + getpoint(cnt) + "," + laps(cnt) + "," + times(cnt) + "\n" loop html_text += "</ul>\n</body>\n</html>\n" nkfcnv html_text,html_text,"Sw" //HTML出力 notesel html_text notesave gpname_now + ".html" noteunsel await 1 // csv出力 notesel csv_text notesave gpname_now + ".csv" noteunsel await 1 mes "save html file" return stop