【テクニカル】PythonによるITTF-Webスクレイピングの概略
PythonによるITTF-Webスクレイピングの概略をメモしておきます。
Webスクレイピングとは、表示されるWebページの中から、狙ったデータを取り出す技術です。現在、「コマンドラインでのデータ処理」に対しては、Pythonが最も適したプログラミング言語となっています。そのため、WebスクレイピングもPythonが適している他、スクレイピング後のイロレーティング算出や勝敗シミュレーションにもPythonが使えます。AIを活用した高度な予測もPythonで可能であり、将来的には取り組みたいと考えています。
なお、蛇足ですが、よく、「どのプログラミング言語を習得すべきか」という議論があるのですが、全くナンセンスです。なぜなら、やりたいこと(作りたいプログラム)に応じて、適したプログラミング環境(言語や周辺ライブラリ)がある程度限定されるからです。すなわち、やりたいことに応じて習得すべきプログラミング言語が異なります。
さて、女子卓球イロレーティングを求めるには、以下のデータを取得しなければなりません。2019年までは、全て、ITTFのstatsページから取得できたのですが、2020年になって、取得場所や方法がそれぞれ異なるようになりました。
項 | 取得データ | 取得方法 |
---|---|---|
1 | 最新ランキングでのプレイヤー一覧 | 新ランキングページでcsvダウンロード |
2 | トーナメント一覧 | statsページで一覧表示 |
3 | プレイヤーごとの出場トーナメント | 新ランキングページのプレイヤー情報表示 |
4 | プレイヤーごとの試合結果 | 新ランキングページのプレイヤー情報表示(トーナメントごとに表示可能) |
それぞれ、Pythonで取得できます。
項1についてはcsvダウンロードは手動で行い、csvからの取り込みのみPythonで自動化します。
項3と項4は動的なページの取得が必要で、最も高度かつ典型的なWebスクレイピングとなります。項2は、項3と項4と比べると簡単です。よって、説明のための例として、項4に関する主要なソースを示します(細かいところは省略しています)。
# 説明1 from selenium import webdriver from selenium.webdriver.chrome.options import Options import chromedriver_binary # 説明2 from bs4 import BeautifulSoup # 説明3 options = Options() options.binary_location = '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary' options.add_argument('--headless') # 説明4 matches = [] # 説明5 for player in players: for tournament_id in keys: # 説明6 while True: # 説明7 driver = webdriver.Chrome(options=options) driver.get('https://ranking.ittf.com/#/players/profile/{}/matches/{}'.format(player.id, tournament_id)) # 説明8 time.sleep(5) # 説明9 html = driver.page_source driver.quit() # 説明10 soup = BeautifulSoup(html, "lxml").find('body').find(class_='results-table') # 説明11 if soup is not None: break print('Failed to get. Waiting 30 seconds to retry to get the same page ...') time.sleep(30) # 説明12 while True: # 説明13 soup = soup.find_next(class_='results-item') # 説明14 if soup is None: break # 説明15 match = Match() match.tournamentID = tournament_id match.type = soup.find('strong').get_text(strip=True) match.round = soup.find(class_='table-name').get_text(strip=True) soup_name = soup.find(class_='name') match.playerA_name = soup_name.find('a').get_text(strip=True) soup_name = soup_name.find_next(class_='name') match.playerX_name = soup_name.find('a').get_text(strip=True) soup_res = soup.find(class_='score-item score-total') match.resA = soup_res.find('span').get_text(strip=True) soup_res = soup_res.find_next(class_='score-item score-total') match.resX = soup_res.find('span').get_text(strip=True) # 説明16 matches.append(match)
ソースに記載している「説明」箇所の説明です。
項 | 説明内容 |
---|---|
1 | 複雑な動的Webページを取得するには、実際のブラウザを自動制御する必要があります。そのためのライブラリを宣言します。なお、スクレイピングという用語は厳密には説明2のことを呼び、本項はクローリングと呼びます。 |
2 | 取得したWebページデータのタグ情報をもとに、狙ったデータを抜き出す必要があります。そのためのライブラリを宣言します。BeautifulSoupという面白い名前のライブラリです。 |
3 | クローリングのための初期設定となるおまじないです。 |
4 | スクレイピングした結果を受け取る配列(Pythonでは「リスト」と呼びます)を準備します。 |
5 | クローリング・スクレイピングを何度も繰り返してたくさんの試合情報を取得するための、ループ宣言です。ここに出てくる変数オブジェクトの宣言は、説明の本質ではありませんので省略しています。 |
6 | クローリングはネットワークや相手サーバーの調子により、必ずしも1発で成功するとは限りません。そのため、繰り返しできるようにループを宣言します。説明5はたくさんの成功結果を得るためのループでしたが、本項は失敗した場合にリトライするためのループであり、役割は異なります。 |
7 | クローリングしてWebページを取得します。説明5のループ変数によって取得するWebページを変えています。 |
8 | Webページが表示されるまで待ちます。この値を短くしすぎると表示されないですし、長すぎると全体の時間がかかってしまいます。適当な値にしつつ、稀に表示されなかった場合の対応を別に準備しておきます。 |
9 | クローリングの後処理です。 |
10 | クローリング結果をスクレイピングするための準備です。 |
11 | うまくWebページが取得できていたら、説明6のループを抜けて、説明12以降のスクレイピング処理に移ります。うまく取得できていなかったら、少し長い時間待って、説明6のループを繰り返して、リトライします。 |
12 | スクレイピングのためのループです。1度にクローリングしたWebページからは、複数の試合情報をスクレイピングするため、ループをセットします。 |
13 | 試合情報を1試合ずつスクレイピングします。 |
14 | 試合情報が無い、ということはすなわち、このWebページの試合情報を全てスクレイピング完了した、ということなので、説明12のループを抜けます。 |
15 | 得られた試合情報をさらに細かくスクレイピングして、対戦相手、何回戦か、得失セットなど、必要なパラメータを得ます。 |
16 | 得られた試合情報を配列に追加して、大きく戻って説明5のループを繰り返します。 |
いかがでしょうか。ご参考になりましたら幸いです。
なお、Webスクレイピングは、ターゲットのWebに大量にアクセスするため、慎重に行う必要があります。開発する際は注意してください。