Edited at

ダートグレード競走のスケジュール一覧をiCalendar形式にする


はじめに

皆さん、競馬、楽しんでますか?(笑)

JRAが重賞スケジュールをiCalendar形式で公開してくれている、という話はこちらの記事にも書きましたが、NAR(地方競馬全国協会)の方でも交流重賞のスケジュールを公開してくれている…ものの残念ながら通常のwebページでの情報公開でそのままではGoogleカレンダー等には取り込むことができません。

そこで公開していただいている情報をもとにiCalendar形式ファイルを生成するプログラムを書いてみました。

※昨年、自分のブログの方で公開していましたが、2018年専用になっていたため、修正の上、こちらに掲載させていただきます。


動作確認環境

以下で動作確認しています。


  • Python 3.7.2

  • beautifulsoup4 4.7.0

  • icalendar 4.0.3

  • requests 2.21.0


実装

早速ですが、こんな感じに実装しました。

ソースはGitHubにおいてあります。

こちらの記事には何箇所か抜粋して記載します。


メイン関数

序盤はargparseを使った引数処理なので省略。


dirtschedule2ics.py

    ical = Calendar()

for race in get_racelist(response.content):
ical.add_component(race2event(race))

args.output.write(ical.to_ical().decode('utf-8'))


icalendar.Calendarのインスタンスに、race2event(後述)で作成したicalendar.Eventのインスタンスをadd_componentで追加、という動きです。

あとは、Calendar.to_ical()はバイト列を返すので文字列にして出力…という流れです。


get_racelist、get_race


dirtschedule2ics.py

def get_racelist(html):

"""レース情報のリストの取得."""
soup = BeautifulSoup(html, 'html.parser')
race = soup.findAll('li', class_='race')
year_line = soup.h3.string
match = re.fullmatch(r'レース一覧((\d{4})年)', year_line)
year = int(match.group(1))

return [x for x in (get_race(x, year) for x in race)
if not x.course.startswith('JRA')]

def get_race(race, year):
"""レース情報の取得."""
grade_code = race.get('class')[1]
grade_table = {'jpn1': 'JpnⅠ', 'jpn2': 'JpnⅡ', 'jpn3': 'JpnⅢ',
'g1': 'GⅠ', 'g2': 'GⅡ', 'g3': 'GⅢ', 'jpn1Central': 'JpnⅠ',
'g1Local': 'GⅠ'}
grade = grade_table[grade_code]
date_string = race.find('p', class_='date').string
date_match = re.fullmatch(r'(\d{1,2})/(\d{1,2})(.+)', date_string)
date = datetime(year, int(date_match.group(1)), int(date_match.group(2)))
name = race.find('p', class_='name').string
course = race.find('p', class_='course').string.split()[0] + '競馬場'

Race = namedtuple('Race', ['grade', 'date', 'name', 'course'])

return Race(grade, date, name, course)


BeautifulSoupと正規表現を使った泥臭い処理で1レースずつのレースの情報を切り出す、ということを行っています。

get_raceの最後でnamedtupleを返していますが、今回はPython 3.7系なので、dataclassを使う方がよかったかもしれません。

なお、この予定表にはJRAのダート重賞も含まれており、そのまま使ってしまうと、JRA公開の重賞予定と重複してしまうので、JRAのレースは除外するようにしています。


race2event


dirtschedule2ics.py

def race2event(race):

"""レース情報をiCalendarのイベントに変換する."""
event = Event()
event.add('SUMMARY', race.name + '(' + race.grade + ')')
event.add('DTSTART', vDate(race.date))
event.add('DTEND', vDate(race.date))
event.add('LOCATION', race.course)
event.add('TRANSP', 'TRANSPARENT')
return event

1つずつのレース情報をiCalendarのEVENTに変換。SUMMARYの文字列がカレンダーでの予定の件名、DTSTARTとDTENDで予定の開始終了、LOCATIONの文字列で場所を表現できます。

こちらの記事で書いたように、「予定なし」として取り込むため、TRANSPをTRANSPARENTにしています。


最後に

競馬もコンピュータを使って快適に楽しみましょう!(笑)