C# .NetCoreで、ボートレース結果の解析 スタ展スクレイピング


やらなきゃならないこと

ボートレース公式サイトからスタート展示情報のスクレイピングを行う。

開発環境

Windows10
Visual Studio Community 2019
C# .Net Core 3.1

注意

スクレイピングは取得先のサーバの過負荷にならないように注意して扱うこと。
また、スクレイピングに関する規定があるため、確認は行うこと。

.NetCoreでのスクレイピング

参考:https://www.casleyconsulting.co.jp/blog/engineer/183/

大まか流れ

  • URLに対してのRequestを作成
  • URLに対してRequestを投げResponseを取得。
  • 必要な内容をパターンで検出する。

Requestを生成

var req = (HttpWebRequest)System.Net.WebRequest.Create(string url)

でURLに対するリクエストを生成する。

Requestに対するResponseを取得

var resreq = (HttpWebResponse)req.GetResponse()

Responseを読み込むためにStreamを取得

var resSt = resreq.GetResponseStream()

Streamを使いHTMLを読み込む。

string htmlData = "";
using( var sr = new StreamReader(resSt, Encoding.UTF8)) )
{
   htmlData = sr.ReadToEnd()
}

まとめると

static public string GetHtmlString(string url)
{
    string rtn = "";
    var webReq = (HttpWebRequest)WebRequest.Create(url);
    using (var webResponse = (HttpWebResponse)webReq.GetResponse())
    using (var responseStream = webResponse.GetResponseStream())
    using (var streamReader = new StreamReader(responseStream, Encoding.UTF8))
    {
        rtn = streamReader.ReadToEnd();
    }
    return rtn;
}

実際にやる

直前情報のURLを対象に実際にやってみる。

URLパターン

https://www.boatrace.jp/owpc/pc/race/beforeinfo?rno=[RaceNum]&jcd=[RacePlace]&hd=[RaceDate]
  • [RaceNum]:レース番号(1,2,3,4,5,6,7,8,9,10,11,12)
  • [RacePlace]:開催場下2桁固定(01,02,03,.....,21,22,23,24)
  • [RaceDate]:開催年月日8桁(yyyymmdd)

例:2020年9月31日 ボートレース戸田 8R の場合(実際あるかは知らない)

https://www.boatrace.jp/owpc/pc/race/beforeinfo?rno=8&jcd=02&hd=20200931

HTMLのパターン

実際に取得しパターンを確認してみた。
以下の部分がスタート展示の情報。

<tr>
  <td colspan="3">
              <div class="table1_boatImage1">
                <span class="table1_boatImage1Number is-type1">1</span>
              <span class="table1_boatImage1Line"><span class="table1_boatImage1Boat" style="left: 59%;"><img src="/static_extra/pc/images/img_boat2_1.png" width="54" height="27" alt=""></span></span>
                <span class="table1_boatImage1Time">.06</span>
          </div><!-- /.table1_boatImage1 -->
  </td>
</tr>
<tr>
  <td colspan="3">
              <div class="table1_boatImage1">
                <span class="table1_boatImage1Number is-type2">2</span>
              <span class="table1_boatImage1Line"><span class="table1_boatImage1Boat" style="left: 37%;"><img src="/static_extra/pc/images/img_boat2_2.png" width="54" height="27" alt=""></span></span>
                <span class="table1_boatImage1Time">.28</span>
          </div><!-- /.table1_boatImage1 -->
  </td>
</tr>
<tr>
  <td colspan="3">
              <div class="table1_boatImage1">
                <span class="table1_boatImage1Number is-type3">3</span>
              <span class="table1_boatImage1Line"><span class="table1_boatImage1Boat" style="left: 56%;"><img src="/static_extra/pc/images/img_boat2_3.png" width="54" height="27" alt=""></span></span>
                <span class="table1_boatImage1Time">.09</span>
          </div><!-- /.table1_boatImage1 -->
  </td>
</tr>
<tr>
  <td colspan="3">
              <div class="table1_boatImage1">
                <span class="table1_boatImage1Number is-type4">4</span>
              <span class="table1_boatImage1Line"><span class="table1_boatImage1Boat" style="left: 42%;"><img src="/static_extra/pc/images/img_boat2_4.png" width="54" height="27" alt=""></span></span>
                <span class="table1_boatImage1Time">.23</span>
          </div><!-- /.table1_boatImage1 -->
  </td>
</tr>
<tr>
  <td colspan="3">
              <div class="table1_boatImage1">
                <span class="table1_boatImage1Number is-type5">5</span>
              <span class="table1_boatImage1Line"><span class="table1_boatImage1Boat" style="left: 54%;"><img src="/static_extra/pc/images/img_boat2_5.png" width="54" height="27" alt=""></span></span>
                <span class="table1_boatImage1Time">.11</span>
          </div><!-- /.table1_boatImage1 -->
  </td>
</tr>
<tr>
  <td colspan="3">
              <div class="table1_boatImage1">
                <span class="table1_boatImage1Number is-type6">6</span>
              <span class="table1_boatImage1Line"><span class="table1_boatImage1Boat" style="left: 38%;"><img src="/static_extra/pc/images/img_boat2_6.png" width="54" height="27" alt=""></span></span>
                <span class="table1_boatImage1Time">.27</span>
          </div><!-- /.table1_boatImage1 -->
  </td>
</tr>

上から1,2,3,4,5,6コースとなる(艇番ではなく)

競番の検出はここで行う。

<span class="table1_boatImage1Number is-type[Course]">[Value]</span>

- [Course]:コース
- [Value]:枠番

タイムの検出はここで行う。

<span class="table1_boatImage1Time">[Time]</span>
  • [Time]:スタートタイム(.XX)

実際にまとめると以下の通りとなる。

        static public string[] SearchTenjiSt(string html)
        {
            var lines = html.Split('\n');
            string[] rtn = new string[12] { "", "", "", "", "", "", "", "", "", "", "", "" };

            int num = 0;
            foreach (var line in lines)
            {
                if (line.Contains("table1_boatImage1Number"))
                {
                    var temp = line.Replace("<", ">");
                    var temp2 = temp.Split(">");
                    rtn[num++] = temp2[2];
                }
                if (line.Contains("table1_boatImage1Time"))
                {
                    var temp = line.Replace("<", ">");
                    var temp2 = temp.Split(">");
                    rtn[num++] = temp2[2];
                }
            }
            if (num == 0)
            {
                rtn = null;
            }
            return rtn;
        }

実行結果

まとめることが可能になった。

2019,10,01,5,1,1,.06,2,.28,3,.09,4,.23,5,.11,6,.27,

まとめ

スクレイピングが可能になった。
試しに1年間分を取得間隔2.5秒で実施したら、3日かかった。

次回は今まで取得したデータをまとめていきたいと思う。