LoginSignup
27
34

More than 5 years have passed since last update.

POG上達のためのデータ解析1 〜Pythonでwebスクレイピング〜

Last updated at Posted at 2015-12-29

はじめに

競馬にはPOGという遊びがある。
これは架空の馬主となって所有馬の活躍度を競うものである。一般的にはデビューからダービーまでの期間の獲得賞金を活躍度の指標とすることが多い。

筆者はかれこれ5年ほどPOGを続けている。
これまでは、毎年4月頃になると発売される青本を頼りに、なんとなく良さ気な馬を選んできた。

しかしながら成績は燦々たるもので今年に至っては10頭中まだ2頭しか勝ち上がっていない。

このうだつのあがらない状況を打破すべく、データ解析に本腰を入れる事にした。ただし筆者には最近流行りの機械学習を自在に操るほどのスキルがない。そこで、ここでは馬の基本情報(厩舎、生産者、血統)とPOG期間中の獲得賞金との間にある因果関係を見出すことを当面の目標としたい。

ここで重要なのが「POG期間中の獲得賞金」である。筆者の知る限り、この情報を公開しているwebサイトは存在しない。もしかしたら有料で提供している機関はあるかもしれないが、費用に見合う効果があるか不明な状況では手を出したくない。

よって、ここではnetkeibaから馬の基本情報や出走歴のデータを取得し、出走歴データを使ってPOG期間中の賞金を計算する という手段を取ることとした。

また、使用した言語はPythonである。これは単に筆者が使い慣れているだけである。

ソースコード

2010年〜2013年までの4年間の間に生まれた馬のデータを収集する。
ここでは4コアの並列処理によって、4年分のデータを同時に取得するようにしている。
マシンスペックにもよるだろうが、筆者のMBAでは約1時間ほどでデータを取り終えた。

※2015/12/30編集
サーバアクセス処理の中に10秒間の時間待ち関数を挿入した。
これなら人力でサイトを閲覧する程度の頻度に抑えられるので過度なアクセスにはならないだろう。

MakeUmaDB_151229.py
#!/usr/bin/env python
# encoding: utf-8

import urllib2 as ul
import pandas as pd
import os
import time
import datetime
from lxml import html
import multiprocessing as mp

__PROC__ = 4

def MakeDir(dname):
    if not os.path.exists(dname):
        os.mkdir(dname)
        print 'Make directory:%s' % dname
    else:
        print '%s is exist' % dname

    return 0

def subMakeHorseDB(year):
    # Set Directory
    o_dname = 'horse_db'
    MakeDir(o_dname)

    # horse_prof
    prof_keys = [
        u'馬名',
        u'生年月日',
        u'調教師',
        u'馬主',
        u'生産者',
        u'産地',
        u'セリ取引価格',
        u'父',
        u'母',
        u'母父',
        u'POG期間賞金_半期',
        u'POG期間賞金_通年'
    ]

    # get Uma data from web site
    base_url = 'http://db.netkeiba.com/horse/'
    idx_from = 100000
    idx_to = 111000

    masta_d = {}
    for idx in range(idx_from, idx_to + 1):
        try:
            # get html from web
            time.sleep(10)
            s_idx = str(year)+str(idx).zfill(6)
            url = base_url + s_idx
            src_html = ul.urlopen(url).read()# get html from url
            root = html.fromstring(src_html)

            # show progress
            print 'idx: %s, (%d, %d/%d)' % (s_idx, year, idx, idx_to)

            # not found db
            if root.xpath('//title')[0].text.startswith(u'|'):
                #print 'DB not found'
                continue

            # html parse
            masta_d[s_idx] = {}

            for prof in prof_keys:
                if prof == u'馬名':
                    horse_name = root.xpath('//div[@class="horse_title"]')[0].text_content().split('\n')[1]
                    masta_d[s_idx][prof] = horse_name

                elif prof == u'父':
                    masta_d[s_idx][prof] = root.xpath('//td[@rowspan="2"][@class="b_ml"]')[0].text_content().split('\n')[1]

                elif prof == u'母':
                    masta_d[s_idx][prof] = root.xpath('//td[@rowspan="2"][@class="b_fml"]')[0].text_content().split('\n')[1]
                elif prof == u'母父':
                    masta_d[s_idx][prof] = root.xpath('//td[@class="b_ml"]')[2].text_content().split('\n')[1]
                elif prof == u'POG期間賞金_半期' or prof == u'POG期間賞金_通年':
                    continue
                elif prof == u'生年月日':
                    masta_d[s_idx][prof] = root.xpath('//table[@class="db_prof_table"]/tr/td')[0].text_content()
                elif prof == u'調教師':
                    masta_d[s_idx][prof] = root.xpath('//table[@class="db_prof_table"]/tr/td')[1].text_content()
                elif prof == u'馬主':
                    masta_d[s_idx][prof] = root.xpath('//table[@class="db_prof_table"]/tr/td')[2].text_content()
                elif prof == u'生産者':
                    masta_d[s_idx][prof] = root.xpath('//table[@class="db_prof_table"]/tr/td')[3].text_content()
                elif prof == u'産地':
                    masta_d[s_idx][prof] = root.xpath('//table[@class="db_prof_table"]/tr/td')[4].text_content()
                elif prof == u'セリ取引価格':
                    masta_d[s_idx][prof] = root.xpath('//table[@class="db_prof_table"]/tr/td')[5].text_content()

            # calc POG prize
            prize_all = 0.0
            prize_half = 0.0
            deadline_all = datetime.datetime.strptime('%d-07-01'%(year+3), '%Y-%m-%d')
            deadline_half = datetime.datetime.strptime('%d-01-01'%(year+3), '%Y-%m-%d')

            r_hist = root.xpath('//table[@class="db_h_race_results nk_tb_common"]')
            if len(r_hist) == 0:
                masta_d[s_idx][u'POG期間賞金_半期'] = '%d' % prize_half
                masta_d[s_idx][u'POG期間賞金_通年'] = '%d' % prize_all
            else:
                r_hist_l = root.xpath('//table[@class="db_h_race_results nk_tb_common"]/tbody/tr')
                for race in r_hist_l:
                    r_date = datetime.datetime.strptime(race.text_content().split('\n')[1],'%Y/%m/%d')
                    try:
                        prize = float(race.text_content().split('\n')[-2].replace(',',''))
                    except:
                        prize = 0.0

                    if r_date < deadline_all:
                        prize_all += prize
                    if r_date < deadline_half:
                        prize_half += prize

                masta_d[s_idx][u'POG期間賞金_半期'] = '%.2f' % prize_half
                masta_d[s_idx][u'POG期間賞金_通年'] = '%.2f' % prize_all
        except:
            pass

    # make data frame
    df = pd.DataFrame(masta_d).T
    o_df = pd.DataFrame()

    # sort columns
    for prof in prof_keys:
        o_df = pd.concat([o_df, df[prof]], axis=1)
    o_df.index.name = 'Index'

    o_fname = 'horse_prof_%d.csv' % year
    o_fpath = os.path.join(o_dname, o_fname)
    o_df.to_csv(o_fpath, encoding='utf-8')

def main():

    year_l = [2010, 2011, 2012, 2013]
    pool = mp.Pool(__PROC__)
    pool.map(subMakeHorseDB, year_l)

if __name__ == '__main__':
    main()
    raw_input('Press Enter to Exit¥n')

今後

吐き出されたcsvファイルをこねくり回してPOG必勝の法則を見出したい。

27
34
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
34