競馬成績表データベースを作る(データ取得編)

1.概要

競馬成績表データベースを作るの第二弾は、競馬成績表データの一括自動取得を行います。事前準備編で取得済みの 各レース毎のレース成績表リンクリストを使います。そのリストのリンクを順次読み込み、サイトを自動巡回して 成績表データを取得します。取得できるデータ項目は、日付、コード、R番号、競馬場、開催、発走時間、天気、馬場、レース名、コース、距離、単勝、枠連、馬連、馬単、3連単、1着、2着、3着、頭数の 計20項目となります。プログラムは、Excel VBAで作成します。


2.データの取得手順

成績表データ取得手順は、次の手順で行います。

(1)Yahoo競馬レース結果取得.xlsmを起動します。(ダウンロードは、本連載記事の最終回で提供)
(2)成績表データ取得ボタンをクリックします。
(3)ファイル選択ダイアログが開くので、事前取得済みのレース成績表リンクリストを選択します。
(4)データ取得が開始されますので完了するまで待ちます。
(5)レース成績表リンクリストは分割して準備されていると思うので、続いて取得の場合は、
     手順(2)から繰り返し実行します。
データベース作成時は、分割取得されたデータを全てマージして利用します。
コード体系は、以下のような体系で構成されています。データ取得時は、このコード番号が キーとしてURLのパラメータとなります。
    (例)2002020806
    https://keiba.yahoo.co.jp/race/result/2005020806/
    20 ⇒ 西暦年 YY
    05 ⇒ 競馬場コード (東京)
    02 ⇒ 開催回 (02回)
    08 ⇒ 開催日目(08日目)
    06 ⇒ レース番号 (06R)
    ●パラメータ
    yy : 開催年
    xx : 競馬場コード
    yy : 開催回[第N回]
    zz : 開催日目[N日目]
    rr : レース番号
    ●競馬場コード
    01 : 札幌競馬場
    02 : 函館競馬場
    03 : 福島競馬場
    04 : 新潟競馬場
    05 : 東京競馬場
    06 : 中山競馬場
    07 : 中京競馬場
    08 : 京都競馬場
    09 : 阪神競馬場
    10 : 小倉競馬場

3.ソースコード

Sheet1(成績表)は、シート上の成績表データ取得ボタンとデータ消去ボタンを クリックした時の処理を記述しています。成績表データ取得ボタンがクリックされると、事前取得してある成績表のリンクリストファイル を選択するためにファイル選択ダイアログが開きます。 データ消去ボタンをクリックするとシート上の取得済みのデータを消去します。
'**** 処理開始 ****
Private Sub IENavi_Click()
    Call GetOpenFilename
End Sub

'***** 成績表データ消去 *****
Private Sub NaviDel_Click()
    If Sheets("成績表").Range("B6") <> "" Then
       Range("B6:U6000").SpecialCells(xlConstants, 23).ClearContents
    End If
End Sub
標準モジュール(Seiseki)に、リンクリストを元に順次サイトを巡回させて、 成績表データを取得してシート上に書き出します。ソースコードは冗長ですが、天気や馬場状態が画像ファイルで表示させているため、 テキストデータとして取得するので、画像ファイルのクラス名より翻訳処理をしています。 レース結果の払い戻し番号取得する部分では、複勝の位置が変動するので、枠番の位置から馬番を求めるように処理しています。 頭数がデータとして取得できないので、競走成績一覧の表からカウントして頭数を算出しています。 取得が完了したら、カレントディレクリに日付時刻形式のファイル名として、CSV形式で出力します。
'*********************************************************************************
'*
'*      競馬成績表データ一括取得処理
'*
'*      - IEによるインターネットサイト自動巡回 (Yahooスポーツ : 競馬) -
'*        https://keiba.yahoo.co.jp/race/result/
'*
'*        ●成績表の日程ファイルから取得した過去の成績表リンクファイルを利用
'*
'*
'*********************************************************************************
'* ☆Yahoo!スポーツ競馬 URL コード体系
'*********************************************************************************
'*
'* ●出走表の場合
'*   https://keiba.yahoo.co.jp/race/denma/1205020806/
'*
'* ●成績表の場合
'*   https://keiba.yahoo.co.jp/race/result/1205020806/
'*
'* ●開催日程の場合
'*   https://keiba.yahoo.co.jp/schedule/list/2012/?month=1
'*
'* (例)
'*  12 ⇒ 西暦年 YY
'*  05 ⇒ 競馬場コード (東京)
'*  02 ⇒ 開催回 (02回)
'*  08 ⇒ 開催日目(08日目)
'*  06 ⇒ レース番号 (06R)
'*
'* ●パラメータ
'*   yy : 開催年
'*   xx : 競馬場コード
'*   yy : 開催回[第N回]
'*   zz : 開催日目[N日目]
'*   rr : レース番号
'*
'* ●競馬場コード
'*   01 : 札幌競馬場
'*   02 : 函館競馬場
'*   03 : 福島競馬場
'*   04 : 新潟競馬場
'*   05 : 東京競馬場
'*   06 : 中山競馬場
'*   07 : 中京競馬場
'*   08 : 京都競馬場
'*   09 : 阪神競馬場
'*   10 : 小倉競馬場
'*
'* ●過去データ(成績表)の場合
'*   2000年1月29日(土) 10:00 1回東京1日
'*   https://keiba.yahoo.co.jp/race/result/0005010101/
'*
'*********************************************************************************

'32bit版Excelの場合は、ptrsafe部分を削除して下さい。
' sleep関数(API)の宣言
Public Declare ptrsafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
' ShowWindow(API)の宣言
Public Declare ptrsafe Function ShowWindow Lib "user32" (ByVal hwindow As Long, ByVal cmdshow As Long) As Long

Dim objIE  As Object
Dim obj    As Object

'***** 自動巡回リストファイル選択ダイアログ *****
Public Sub GetOpenFilename()
    Dim FileName As Variant
    Dim PathName As String

    Worksheets("成績表").Activate
    Sheets("成績表").Select

    Application.DefaultFilePath = ThisWorkbook.Path
    PathName = ThisWorkbook.Path & "¥result¥"
    ChDir PathName

    'ファイルを開くダイアログ
    FileName = _
        Application.GetOpenFilename( _
             FileFilter:="テキストファイル(*.txt),*.txt" & _
                         ",CSVファイル(*.csv),*.csv" _
           , FilterIndex:=1 _
           , Title:="ファイルの選択" _
           , MultiSelect:=False _
            )

    If FileName <> False Then
       If Sheets("成績表").Range("B6") <> "" Then
          Range("B6:U6000").SpecialCells(xlConstants, 23).ClearContents
        End If
        Call IE_open(FileName)
    End If
End Sub

'***** 競馬成績表(Yahooスポーツ : 競馬) サイト自動巡回処理 *****
Private Sub IE_open(ByVal FileName As String)
    Dim URL        As String
    Dim Path       As String
    Dim i          As Integer
    Dim p          As Integer
    Dim strText    As String
    Dim strArray() As String
    Dim houseStr   As String

    On Error Resume Next

    Worksheets("成績表").Activate
    Sheets("成績表").Select

    Set objIE = CreateObject("InternetExplorer.Application")
    objIE.Visible = False

    '***** 初期起動ページ表示 *****
    URL = "https://keiba.yahoo.co.jp/"
    objIE.Navigate URL

    Do While objIE.ReadyState <> 4
       Do While objIE.Busy = True
          DoEvents
       Loop
    Loop

    '***** ウィンドウ制御 (Max : 3, Min : 2) *****
    ret = ShowWindow(objIE.Hwnd, 2)

    objIE.Visible = True

    '***** ファイル名を保存 *****
    If FileName = "" Then
       MsgBox "リストファイルが選択されていません。"
       objIE.Quit
       Set objIE = Nothing
       Exit Sub
    End If
    
    '***** 選択されたファイルの有無を確認 *****
    'With CreateObject("Scripting.FileSystemObject")
    '   If FileExists(FileName) = False Then
    '      MsgBox "ファイルが存在しません。"
    '      objIE.Quit
    '      Set objIE = Nothing
    '      Exit Sub
    'End With
    
    '***** ファイル名を退避 *****
    Path = FileName
    
    '***** ファイルを開く *****
    Open Path For Input As #1

    '▼▼▼▼▼ ここから繰り返し処理の開始 ▼▼▼▼▼

    p = 6   ' 行の開始位置
    'Application.ScreenUpdating = False

    Do Until EOF(1)

       ' ファイルを1行読み込み
       Line Input #1, URL
       
       If URL = "" Then Exit Do
       
       If Mid(URL, 1, 5) <> "https" Then
          MsgBox "リストファイルにURLが含まれていません。"
          objIE.Quit
          Set objIE = Nothing
          Exit Sub
       End If
       ' 空行がある場合は処理を終了
       If URL = "" Then Exit Do
       ' ナビゲート開始
       objIE.Navigate URL

       ' ページが完全に表示されるまで待つ
       Do While objIE.ReadyState <> 4
          Do While objIE.Busy = True
             DoEvents
          Loop
       Loop

       'Sleep (100)

       Application.StatusBar = objIE.Document.Title

       '▼▼▼▼ データ取得処理 ▼▼▼▼
       
       '***** レースコード *****
       Cells(p, 3) = Replace(Right(URL, 11), "/", "")
       
       '***** レース番号を取得 (1R etc) *****
       For Each obj In objIE.Document.getElementsByTagName("td")
           If obj.ID = "raceNo" Then
              strText = obj.innerText
              Cells(p, 4) = strText
              Exit For
           End If
       Next
      
       '***** 開催日、開催場所、発走時間を取得 *****
       For Each obj In objIE.Document.getElementsByTagName("p")
           If obj.ID = "raceTitDay" Then
              houseStr = obj.innerText
              ' 取得内容を分解
              strArray = Split(houseStr, "|")
              '● 開催日を編集 (yyyy/mm/dd 形式)
              strText = strArray(0)
              strText = Mid(strText, 1, InStr(strText, "(") - 1)
              strText = Replace(strText, "年", "/")
              strText = Replace(strText, "月", "/")
              strText = Replace(strText, "日", "")
              Cells(p, 2) = strText
              '● 開催場所を編集 (XX回中山XX日)
              strText = Trim(strArray(1))
              Cells(p, 6) = strText
              '● 競馬場
              strText = Mid(strText, InStr(strText, "回") + 1, 2)
              Cells(p, 5) = strText
              '● 発走時間
              strText = Trim(strArray(2))
              strText = Mid(strText, 1, InStr(strText, "発") - 1)
              Cells(p, 7) = strText
              Exit For
           End If
       Next

       '***** 天気 (画像ファイルのクラス名より判定) *****
       For Each obj In objIE.Document.getElementsByTagName("img")
           Select Case obj.className
              Case "spBg hare"
                 Cells(p, 8) = "晴"
                 Exit For
              Case "spBg kumori"
                 Cells(p, 8) = "曇"
                 Exit For
              Case "spBg ame"
                 Cells(p, 8) = "雨"
                 Exit For
              Case "spBg yuki"
                 Cells(p, 8) = "雪"
                 Exit For
              Case "spBg koyuki"
                 Cells(p, 8) = "小雪"
                 Exit For
              Case "spBg kosame"
                 Cells(p, 8) = "小雨"
                 Exit For
           End Select
       Next

       '***** 馬場 (画像ファイルのクラス名より判定) *****
       For Each obj In objIE.Document.getElementsByTagName("img")
           Select Case obj.className
              Case "spBg ryou"
                 Cells(p, 9) = "良"
                 Exit For
              Case "spBg yayaomo"
                 Cells(p, 9) = "稍重"
                 Exit For
              Case "spBg omo"
                 Cells(p, 9) = "重"
                 Exit For
              Case "spBg furyou"
                 Cells(p, 9) = "不良"
                 Exit For
           End Select
       Next

       '***** レース名 *****
       For Each obj In objIE.Document.getElementsByTagName("h1")
           If obj.className = "fntB" Then
              strText = obj.innerText
              Cells(p, 10) = strText
              Exit For
           End If
       Next

       '***** コース、距離を取得 *****
       For Each obj In objIE.Document.getElementsByTagName("p")
           If obj.ID = "raceTitMeta" Then
              houseStr = obj.innerText
              ' 取得内容を分解
              strArray = Split(houseStr, "|")
              strText = Trim(strArray(0))
              '● コース (芝、ダート)
              strText = Mid(strText, 1, InStr(strText, " ") - 1)
              Cells(p, 11) = strText
              '● 距離
              strText = Trim(strArray(0))
              strText = Mid(strText, InStr(strText, " ") + 1, 4)
              Cells(p, 12) = strText
              Exit For
           End If
       Next

       '***** レース結果の払い戻し番号取得 - 1 *****
       ' ▼複勝の位置が変動するので、枠番の位置から馬番を求める
       i = 0
       j = 0
       For Each obj In objIE.Document.all
           Select Case obj.tagName
              Case "TR", "TD", "TH"
                 If obj.offsetparent.className = "resultYen" Then
                    Select Case obj.tagName
                       Case "TR"
                       Case "TD", "TH"
                          '***** 単勝,枠連,馬連 *****
                          If obj.className = "txC resultNo" Then
                             i = i + 1
                             '● 単勝
                             If i = 1 Then
                                Cells(p, 13) = obj.innerText
                             End If
                             '● 枠連
                             If i > 2 And j = 0 And InStr(obj.innerText, "-") <> 0 Then
                                 Cells(p, 14) = Replace(obj.innerText, "-", "-")
                                j = i
                                j = j + 1
                             End If
                             '● 馬連
                             If i = j And InStr(obj.innerText, "-") <> 0 Then
                                Cells(p, 15) = Replace(obj.innerText, "-", "-")
                                Exit For
                             End If
                          End If
                    End Select
                 End If
           End Select
       Next

       '***** レース結果の払い戻し番号取得 - 2 *****
       i = 0
       For Each obj In objIE.Document.all
           Select Case obj.tagName
              Case "TR", "TD", "TH"
                 If obj.offsetparent.className = "resultYen noMgn" Then
                    Select Case obj.tagName
                       Case "TR"
                       Case "TD", "TH"
                          '***** 馬単,3連単 *****
                          If obj.className = "txC resultNo" Then
                             i = i + 1
                             '● 馬単
                             If i = 4 Then
                                houseStr = obj.innerText
                                If houseStr <> "" Then
                                   Cells(p, 16) = Replace(houseStr, "-", "-")
                                End If
                             End If
                             '● 3連単
                             If i = 6 Then
                                houseStr = obj.innerText
                                If houseStr <> "" Then
                                   Cells(p, 17) = Replace(houseStr, "-", "-")

                                   '***********************************************************
                                   ' 3連単の結果から、1着,2着,3着の馬番を取得
                                   ' 過去のレースではデータなしのため、別途取得とする
                                   ' 2004年 9月11日の開催レースから
                                   ' 第4回中山競馬・第4回阪神競馬・第2回札幌競馬初日
                                   '***********************************************************
                                
                                   '***** 取得内容を分解 *****
                                   strArray = Split(Cells(p, 17).Value, "-")
                                   Cells(p, 18) = Cells(p, 13).Value  '●1着馬番号
                                   Cells(p, 19) = strArray(1)         '●2着馬番号
                                   Cells(p, 20) = strArray(2)         '●3着馬番号
                                End If
                                Exit For
                             End If
                          End If
                    End Select
                 End If
           End Select
       Next

       '***** 頭数をカウントして求める *****
       i = 0   ' ※ 頭数をカウントして算出
       For Each obj In objIE.Document.all
           Select Case obj.tagName
              Case "TR", "TD", "TH"
                 If obj.offsetparent.className = "dataLs mgnBS" Then
                    Select Case obj.tagName
                       Case "TR"
                          ' 空ループ
                          i = i + 1
                       Case "TD", "TH"
                    End Select
                 End If
           End Select
       Next
       '● 頭数
       Cells(p, 21) = i - 1
       
       '***** 行をインクリメント *****
       p = p + 1
    Loop
    '***** ファイルを閉じる *****
    Close #1

    objIE.Quit
    Set objIE = Nothing
    Set obj = Nothing

    '▼▼▼▼ ファイル名を作成 ファイル名は自分のパス+¥SyyyymmddHHMMSS.csvとして自動生成 ▼▼▼▼
    Dim FNAME As String
    '***** ファイル名を合成 (年月日時分秒) *****
    FNAME = "S" & Year(Date) & Right("0" & Month(Date), 2) & Right("0" & Day(Date), 2) & _
             Right("0" & Hour(Time), 2) & Right("0" & Minute(Time), 2) & Right("0" & Second(Time), 2) & ".csv"
    
    Dim strFNAME As String   ' ファイル名保存用
    strFNAME = ThisWorkbook.Path & "¥" & FNAME

    '***** テーブルデータからCSV形式ファイルを出力する *****
    Call MAKE_CSV_FILE(strFNAME, Range(Cells(6, 2), Cells(p - 1, 21)))

    Application.ScreenUpdating = True
    Application.StatusBar = False
    Sheets("成績表").IENavi.Enabled = True
    Sheets("成績表").NaviDel.Enabled = True

    MsgBox "データ取得処理が完了しました"

    Range("B6").Select
End Sub

'***** ファイルを開きカンマ区切りのファイルを作成する *****
Sub MAKE_CSV_FILE(strFNAME As String, objHANI As Range)

    '***** ファイルをオープンする *****
    Dim FNO As Integer                ' ファイル番号
    FNO = FreeFile                    ' 空いてるファイル番号を取出す
    Open strFNAME For Output As #FNO  ' テキストファイルを新規作成

    '***** 行、列でループを作る *****
    Dim Y As Integer
    Dim x As Integer
    For Y = 1 To objHANI.Rows.Count                   ' 行のループ
        Print #FNO, Trim(objHANI.Cells(Y, 1).Value);  ' 先頭項目の出力
        For x = 2 To objHANI.Columns.Count            ' 列のループ
            Print #FNO, Trim(",");
            Print #FNO, Trim(objHANI.Cells(Y, x).Value);
        Next x
        Print #FNO, Trim("")  ' 改行のみ出力
    Next Y

    '***** ファイルクローズ *****
    Close #FNO
End Sub

4.ダウンロード

提供するソースコードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) とします。自由に利用して頂いてかまいません。
今回の記事で紹介してるプログラムは、本シリーズの連載最後にまとめてダウンロード提供する予定としています。 少しずつ記事をアップしていきますので、しばらくお付き合いください。

■関連記事
・Excelでデータベース(SQLite3)を扱う
・複数のファイルをマージする
・競馬成績表データベースを作る(事前準備編)
・競馬成績表データベースを作る(データベース編)
・競馬成績表データベースを作る(ツール-Excel編)
・競馬成績表データベースを作る(ツール-HSP編)

コメント

このブログの人気の投稿

Excelアドインで日本語形態素解析

階層構造JSONファイルの作成

HSPでコマンドプロンプトを制御する

TOP