前回はグランプリごとのリザルトページをデータ(CSV)化と簡易htmlページを出力まで完成しました。今回は全戦の結果をまとめたCSVの出力と、重複した場合の処理をプログラミングしていきたいと思います。
ドライバー単位でレースの結果データからポイントとなるものを全戦結果CSVに組み込んでいく予定です。前回のおさらいになりますが、拾うデータは
- 順位
- ポイント
- ラップ(タイム差)
の3項目としました。さっそく全戦csvに取り掛かってまいりましょう。
2016年の全戦をデータ化してみる(続き)
前回のプログラムを利用していきます。部分部分を取り出して書いていきますので、全コードを確認したい場合はページの一番下をみてみてくださいな(コピペ)。
全戦CSV用にセッティング
最初に入れ物を用意しておきます。
allresult_csv = ""
さらに、元々全戦CSVファイルが存在しているかの確認もしておく必要があります。これは以前のURL抽出と同じ方法が使えば解決。ただし、ファイル名を決めておく必要があります。ここでは
allresult.csv
という名前にしました。では、この名前のファイルが存在するかをチェックするコードを記述しましょう。
dirlist chk, "allresult.csv" await 1 if (stat = 0) { (存在しない場合に入れ物を用意し、基本的な中身を入れておく) } else { notesel allresult_csv noteload "allresult.csv" noteunsel }
これでチェック部分はOK
基本的な中身の記述
上の部分にて「基本的な中身」を用意しなければいけません。元々あって欲しい中身は
- 項目名(見出し部分)
- トライバー名
なのですが、ドライバーはシーズン途中での変更やピンチヒッターなんてことも考えられます。なので、初めから用意するのではなく、結果から抽出して加えていくスタイルにしたいと思います。ということで、最初から入れる中身は
allresult_csv = "drivername"
これだけになります。
追加していくデータの成形(項目部分)
まず、項目部分にレース名とあわせて「順位」「ポイント」「ラップ」を追加してあげる必要があります。
(追加する項目の1文) += "," + gpname_now + ":pos,pts,laps"
「追加する項目の1文」という部分なのですが、プログラミングとしては、項目部分だけを取り出して、そこを書き換える形で進め行きたいと考えてます。なので、上に書いたコードは書き換える内容となるわけです。これをもともとの項目部分に上書きしてあげる形となります。そのためには
指定行の追加・変更
noteadd p1,p2,p3
p1=文字列 : 追加・変更をする文字列または変数名
p2=0~(-1) : 追加するインデックス
p3=0~1(0) : 上書きモード指定(0=追加・1=上書き)
これを使います。項目はcsvの一番最初に存在するのでp2のインデックスは「0」、上書きモードなのでp3は「1」になります。
(追加する項目の1文) += "," + gpname_now + ":pos,pts,laps" noteadd (追加する項目の1文), 0, 1
追加していくデータの成形(新規ドライバーのデータ)
次にデータの本体をつくっていきます。まず、ドライバー名がデータに存在するのかどうかを確認して、なければドライバー名の追加という処理を考えていきましょう。存在の確認は「if instr」で行います。
drivername = firstname(cnt) + " " + familyname(cnt) if (instr(allresult_csv, 0, drivername) = -1) : (いない場合の処理)
いない場合の処理はドライバー名から新たに追加する必要があります。また、現在が何戦目なのかによって、レース結果を追加する場所が変わって来る点も考慮しないといけません。その判定方法なのですが、
- 項目部分の数から判断する
という方法で進めてみようと思います。場合分けで考えてみましょう。
【初戦の場合】 項目数はdrivernameのひとつのみ → 項目数 1 【2戦目の場合】 項目数はdrivernameと初戦の3つ → 項目数 1+3 【n戦目の場合】 項目数はdrivernameと{3×(n-1)} → 項目数 1+3(n-1)
項目数の関係は整理できました。実際に項目数を判定するにはsplitを使います。本来はひとつの文を切り分けるのにつかう命令ですが、実行するとstatに切り分けた数が格納されるので、これを利用します。
split (項目の1文), ",", buf comma = stat
で、項目数の数「,」を追加してからデータを入れることで対応できるようになるはずです。
addcomma = "" : repeat comma: addcomma += "," : loop (driver単位) = drivername + addcomma + position(cnt) + "," + getpoint(cnt) + "," + laps(cnt) +"(" + times(cnt) + ")"
追加していくデータの成形(既存ドライバーのデータ)
次にドライバーがデータに存在している場合の処理です。全戦参加している場合は都度追加していくことで問題はないのですが、怪我などで欠場があったりした場合はその部分を補完してあげないといけません。なので、こちらも項目数とドライバー単位データの数を比較しておく必要がありそうです。そして比較した数の分だけコンマを追加してあげればいいので、
split (driverの1文), ",", buf dirver_comma = comma - stat + 1 addcomma = "" : repeat dirver_comma : addcomma += "," : loop (driver単位) += addcomma + position(cnt) + "," + getpoint(cnt) + "," + laps(cnt) +"(" + times(cnt) + ")"
こんな感じになりました。1を追加しているのは、欠場などがない場合でもコンマをひとつ追加する必要があったのでそうしてます。
追加していくデータ3つを組み合わせる
上3つで作成したものを組み合わせてみましょう。実際にプログラミングしていく段階で少し変更をほどこしています。
- ドライバーが存在しない場合の処理はallresult_csvの中身の最後に追加する処理に
- ドライバーが存在するかどうかのチェック(if文)の処理を追加
- 項目部分の一文をあらかじめ取り出して処理しておく
- 項目部分取り出し処理の後に追加処理をする
- repeat~loopを入れ子にするので、データをいったん格納する
- ドライバー部分にもnoteadd処理を追加/ 項目部分取り出し・追加
/* 項目部分取り出し・追加 */ notesel allresult_csv noteget text_line, 0 split text_line, ",", buf comma = stat text_line += "," + gpname_now + ":pos,pts,laps" noteadd text_line, 0, 1 (csv作成のrepeat) /* データの格納しなおし */ drivername = firstname(cnt) + " " + familyname(cnt) data_box = position(cnt) + "," + getpoint(cnt) + "," + laps(cnt) +"(" + times(cnt) + ")" /* ドライバーデータの追加 */ if (instr(allresult_csv, 0, drivername) = -1) { addcomma = "" : repeat comma: addcomma += "," : loop allresult_csv += drivername + addcomma + data_box + "\n" } else { repeat notemax noteget text_line, cnt if (instr(text_line, 0, drivername) ! -1) { split text_line, ",", buf dirver_comma = comma - stat + 1 addcomma = "" : repeat dirver_comma : addcomma += "," : loop text_line += addcomma + data_box noteadd text_line, cnt, 1 } loop } (csv作成のloop) noteunsel
そして、作成したcsvを保存しておきましょう。
// allresult出力 notesel allresult_csv notesave "allresult.csv" noteunsel await 1
これで準備完了。いったん、前回実行したときに生成されたhtmlやcsvを削除して実行してみると
生成も無事できてますし、中身も2戦目で欠場したアロンソの部分など処理できています。大丈夫そうですね。
まとめ
今回はここまで。今のところ2016年での確認なので2017年シーズンになってあらためて実行してみないとわかりませんが、一応全戦結果のcsvファイル作成まで完了しました。次回は、フォルダに大量のファイルが生成されるようになってきてしまったので、これを整理するようにフォルダを用意してそこに保存させる仕組みを考えていきたいと思います。今回もコードを記載します。ぜひ試してみてくださいね。
//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" //#include "hspext.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 } dirlist chk, "allresult.csv" await 1 if (stat = 0) { allresult_csv = "driver Name" } else { notesel allresult_csv noteload "allresult.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 noteunsel //make_csv 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_area 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" /* 項目部分取り出し・追加 */ notesel allresult_csv noteget text_line, 0 split text_line, ",", buf comma = stat text_line += "," + gpname_now + ":pos,pts,laps" noteadd text_line, 0, 1 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" /* データの格納しなおし */ drivername = firstname(cnt) + " " + familyname(cnt) : title gpname_now + ":" + drivername data_box = position(cnt) + "," + getpoint(cnt) + "," + laps(cnt) +"(" + times(cnt) + ")" /* ドライバーデータの追加 */ if (instr(allresult_csv, 0, drivername) = -1) { addcomma = "" : repeat comma: addcomma += "," : loop allresult_csv += drivername + addcomma + data_box + "\n" } else { repeat notemax noteget text_line, cnt if (instr(text_line, 0, drivername) ! -1) { split text_line, ",", buf dirver_comma = comma - stat + 1 addcomma = "" : repeat dirver_comma : addcomma += "," : loop text_line += addcomma + data_box noteadd text_line, cnt, 1 } loop } loop noteunsel 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 // allresult出力 notesel allresult_csv notesave "allresult.csv" noteunsel await 1 mes "save html file" return stop