はじめに
皆さん、競馬、楽しんでますか?(笑)
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を使った引数処理なので省略。
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
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
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にしています。
最後に
競馬もコンピュータを使って快適に楽しみましょう!(笑)