Python スクレピング 競馬サイトから出走馬の過去成績を取得

## 背景

現在、競馬×機械学習のために、
ウェブページから競馬情報をスクレイピングすることに取り組んでいます。

入力内容に出走馬の過去の成績を組み込もうとしています。
そこで競馬のレース結果表から出走馬のページのURLを取得、アクセスし、
出走日の過去の成績を取得することにした。

##構想
![ｓｓ図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503403/28f87d5f-68b9-468c-78cd-49be92b043dc.png)





##サンプルコード 

アイデアは以下の3点。
・取得方法をCSSセレクターで統一
・レース結果表から各馬のURLを取得し、そのURLに掲載されている過去成績を取得
・今回の出走馬は12頭だが、他の馬数でも取得できるように可変長変数を使用

この記事の前に、[Python スクレピング 競馬サイトからレース環境を抽出](https://qiita.com/yusa0827/items/262006b932c50826bd11)を紹介させていただきました。
こちらはレース環境をBeautifulSoupを使いました。
しかしcssセレクターで統一して処理したほうが気持ちがいいので書き換えました。

```python:scraping_of_race_and_past_horse_result.py
import requests
import lxml.html 
import csv

rlt = [] #結果
horse_URL = []#馬の名前を取得する

#スキャルピングでテキスト取得
def get_scarping_data(key_page,css_select_str,*URL):
    #取得したURLの個数分の情報を取得する
    for i in range(len(URL)):
        #URLから文字列を取得       
        r = requests.get(URL[i])#URLを指定 
        r.encoding = r.apparent_encoding #文字化けを防止
        html = lxml.html.fromstring(r.text) #取得した文字列データ
        # 項目を取得
        for css_id in html.cssselect(css_select_str):        
            #レース結果サイトのレース環境
            if key_page == "condition_in_race_page" :
                #Element番号のテキスト 
                css_id = css_id.text_content()
                #内包表記 抽出する際に"天気"は不変であるので条件指定
                css_id_ = [css_id for t in css_id if "天気" in css_id]
                css_id_ = css_id_[0].split('\xa0/\xa0')
                #リストに行のデータ(リストを追加)
                rlt.append(css_id_)
            #レース結果サイトのレース結果
            if key_page == "result_in_race_page" :
                #Element番号のテキスト 
                css_id = css_id.text_content()
                #改行("\n")をもとに分割する
                css_id = css_id.split("\n")
                #内包表記 空の要素を駆逐する　空文字を除く
                css_id_ = [tag for tag in css_id if tag != '']
                #1位はタイム記録なし #強引に加える必要がある 8番目に0
                if len(css_id_) != 13 : css_id_.insert(8,0)
                #リストに行のデータ(リストを追加)
                rlt.append(css_id_) 
            #レースした馬の過去成績サイト
            if key_page == "horse_race_data" : 
                #Element番号のテキスト 
                css_id = css_id.text_content()
                #取得したElement番号の
                css_id = css_id.split("\n")
                #内包表記 空と"\xa0"と"映像"を除去
                css_id_ = [tag for tag in css_id 
                if tag != '' and tag != "\xa0" and tag != "映像" and tag != "厩舎ｺﾒﾝﾄ" and tag != "備考" ]
                #リストに行のデータ(リストを追加)
                rlt.append(css_id_)        
    #抽出結果
    return rlt

#レースした馬のURLを取得
def get_scarping_past_horse_date(URL):            
    response = requests.get(URL)
    root = lxml.html.fromstring(response.content)
    #1~12までの馬の情報を取得
    for i in range(2,14):#2~13  13 - 2 + 1 = 12 
        css_select_str = "div#race_main tr:nth-child({}) > td:nth-child(4) > a".format(i)
        #レースした馬の情報を取得
        for a in root.cssselect(css_select_str):
            horse_URL.append(a.get('href'))
    #抽出結果
    return horse_URL

#まず本家のレースサイトからレース環境を取得
URL = "https://nar.netkeiba.com/?pid=race&id=p201942100701" 
#レース環境を取得
rlt = get_scarping_data("condition_in_race_page","div#main span",URL)
#レース結果を取得
rlt = get_scarping_data("result_in_race_page","#race_main > div > table > tr",URL)
#レースした馬のURLを取得
horse_URL = get_scarping_past_horse_date(URL)
print(len(horse_URL))#出走馬12に対し、出力結果は12
#取得した馬のURLから過去成績を取得
#項目(1行分)を取得
rlt = get_scarping_data("horse_race_data", "#contents > div.db_main_race.fc > div > table > thead > tr",horse_URL[0])
#項目以外の成績データ
rlt = get_scarping_data("horse_race_data", "#contents > div.db_main_race.fc > div > table > tbody > tr",*horse_URL)
#CSVファイルに保存
with open("scraping_of_race_and_past_horse_result.csv", 'w', newline='') as f: 
    wrt = csv.writer(f) 
    wrt.writerows(rlt) #抽出結果の書き込み
```

##実行結果
![sss.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503403/72e2342d-5596-e381-060c-6fb5b8286000.png)


##反省点
・レース結果から馬の情報が掲載されているURLを取得し、過去成績も取得できた。
・過去成績を取得できたが最新の情報も掲載されているので、レースした日を基準に遡った成績を取得する必要がある。
・取得できた情報をニューラルネットワークに入力できるレベルまで行っていない。
・レースした日を自動で取得する。毎回調べない
→方法として、レース情報数値(URLの末端の12桁)を規則性を考慮してインクリメント。

## CSSセレクタの調べ方

スクレイピングするうえで、以下のコード(文字列)を使いこなす必要がある。
(#contents > div.db_main_race.fc > div > table > tbody > tr')
↑ここの部分をどのようにして取得するのかをまとめる。

```python:how_to_search_css_sector.py
for h in html.cssselect('#contents > div.db_main_race.fc > div > table > tbody > tr'):#スクレイピング箇所をCSSセレクタで指定
```


初めはただコピペしたり、そっれっぽいコードを書いてトライしたが、うまく行かない。。。
調べてみるとCSSが絡んでいる。
欲しい項目はどのCSSセレクターで構成されたいるかを知るために、
Chromeの[Copy Css Selector](https://chrome.google.com/webstore/detail/copy-css-selector/kemkenbgbgodoglfkkejbdcpojnodnkg/related)のツールを使う。
![無題4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503403/55fae70e-8aaf-d981-5072-c54863ad7325.png)


インストールすると右クリックでCopy Css Selectorの項目ができる。
欲しい項目でCopy Css Selectorを実行し、テキストに張り付けて確認するとCSSセレクターを知ることができる。
※うまくいかないこともあるかもしれない。

![無題4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503403/27226667-568c-250a-bfd1-eb495f75ae62.png)


他にもディベロッパー ツールを用いて、
欲しい情報範囲で　Copy → Copy Selector　でCCSセクターをコピーできる。
たまにうまくいきます。
![無題5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503403/ce99f919-5fbf-eaac-db47-1ee9c2f28af0.png)

この2つを駆使すれば、SCCセレクターを取得できるはずです。