2019年10月15日火曜日

JRAのWEBページをpythonでスクレイピングしてみる(その1)

 ふとJRAの出走馬データやらオッズデータやらが欲しくなった。Winを使っているならばJRA-VANなどに登録して、JRAのデータを自分が使っているデータベース(SQLServer、Oracle、MySQL、PostgreSQL、SQLiteとか)に落としてくれるアプリケーションをダウンロードしてくれば割と簡単に入手できるのだが、いかんせん普段Winを使わないのでこの方法は用いたくない。

 そんなワケで最近よく使っているpythonを使ってJRAのWEBページからデータを取得することを試してみることにした。

 まずは、"hello, World"代わりに、以下の様なコードを実行してみた。



 ここで使用しているパッケージはrequests、beautifulsoup4で無い場合はあらかじめ用意しておく必要があるだろう。また、beautifulsoup4の中でlxmlを使用するので(BeautifulSoupオブジェクト作成時に’lxml’といパラメータを指定している為)lxmlも必要となる。

 成功するとJRAのトップベージがdata/jra_jp.htmlというファイルに保存された。正直なところファイル保存する前までの処理はブラウザにある「ページのソースを表示」と同じようなものなのでそれほど嬉しくない。

 保存されたファイル(data/jra_jp.html)からオッズのページへのリンクと思われる箇所を探してみると、以下の様な部分が見つかった。



 どうやらdoActionというJavaScript関数でページを遷移させている模様。通常こういった共通関数っぽいものは別ファイルに纏められて<script>タグなどによって読み込まれるので保存されたファイルには現れない。そこでブラウザのF12機能(F12キー押下で起動するからこう呼んでいるが、所謂"開発者ツール"というやつ)を使ってdoActionなる関数の定義を探す。第1引数のアドレスに対して第2引数をパラメータとしてサブミットしているらしい。

 トップページ以降のページはGETなりPOSTなりで取得する必要がありそうだが幸い、requestsには容易にこれらのリクエストを発行する機能が用意されている。

 getは既に使用したように引数にURLを渡すが、キーワード引数としてparams=辞書のような形で指定してやることによってサーバにパラメータを渡すことが出来る。

>>> d = {'name1':'value1', 'name2':'value2'}
>>> r = requests.get('http://localhost/get', params=d)

 リクエストがどの様にエンコードされたかを見るには以下の様にする。

>>> print(r.url)
u'http://localhost/get?name1=vlue1&name2=value2'

 postの場合はキーワード引数にdata=辞書のような形で指定する。
>>> d = {'name1':'value1', 'name2':'value2'}
>>> r = requests.post('http://localhost/post', data=d)

 requestsのクイック・スタートというドキュメントを見るとこの辺のことが色々書いてある。今回は使用しなかったがログインが必要なページなどにアクセスする際にはこのドキュメントに書かれていることが役に立つだろう。

 ここまでで大体の素材は揃ったので以下の様な関数を書いてみた。



 先に見つけた
<a href="#" onClick="doAction('/JRADB/accessO.html','pw15oli00/6D')">オッズ</a>

ならば、JRADoAction('/JRADB/accessO.html', 'pw15oli00/6D')を呼び出す事によって同等な処理を行うことになる(筈?(^^;))。
 ここまで結構、順調に事が進んで来たように書いてきたが色々あった。

 まず、実際のサイトで試す前にそのサイトのデータをごっそりファイルに落としてローカルなマシン上にapachなどのサーバを立て落としたファイルに擬似的にアクセスするようにした。実際のサイトでは開催前、開催レース中、開催レース後、開催後、開催後決定(これは自分的に名付けたので本当は別の名前があるのかも知れないが)日々または時々変わるのでモデル的なデータを固定しておかないと何が良いのか何が悪いのかが判りにくくなる。
 ローカルなサーバであるのでJRADoAction()を呼び出した時に発生したときのリクエストに対応するべき処理(所謂CGI)を 作って本物のサイトと(自分がリクエストする範囲内で)同じになるように調整した。

 それと、ここまで色々な邪念があった…意外と日々これ一つだけやってる訳ではないのでSeleniumとかを使えばもっと楽になるのではないか?とか、doActionの第二引数に指定される謎のキーワード(この場合は'pw15oli00/6D')は規則性があるみたいだからちょっと解析してみようか当等。意外とこれらは簡単ではなく挫折した。

 結局、まじめにトップのページからエレメントを取り出して、まじめにリクエストを発行するのが一番無難だという結論に達した次第。

 次に取り組む必要に迫られるのは、読み出したコンテンツから必要なエレメントを取り出す方法なのだが、これは次に書くことにする。