
前回はグランプリごとのリザルトページをデータ(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