#初めに
スクレイピングのサイトみていると
「URLが変わらないページはSeleniumを使いましょう」
と書いてあるけど、なんとなくSeleniumってダサいよね。
っていうBeautifulSoupユーザーの為の記事。
#導入
今回は競馬ネットというサイトから競馬レースのデータをスクレイピングすることを考えます。
競馬ネット:https://www.netkeiba.com/
サイトの仕様ですが、下図のようにレース詳細検索画面で検索条件を指定すると20件ずつレースが抽出されるというものになっています。
扨、検索結果画面の方ですが20件ずつしか表示されない為、次の20件を取得する為には当然画面下の「次」ボタンで画面遷移する必要があります。
しかし、検索結果のURLは何ページ目だろうと "https://db.netkeiba.com/" であり、requestsでHTMLソースを取得→BeautifulSoupで解析の王道パターンが一見使えないように思えます。
そうなってくるとSeleniumの使用を検討すると思うのですが、前述の通りSeleniumってなんとなくダサい気がしています(私だけ?)。
ということでrequestsを使ってどうにかこのサイトからレースデータを取得できないか検討してみます。
#ネットワークを確認する
上記のサイトについてURLは変わっていなくとも、画面にレース一覧を表示している以上、何かしらの通信を行ってサーバーからレース情報を取得しているはずです。この通信を探します。
まずブラウザのデベロッパー画面を開きます。ChromeならF12キーを押すと開きます。
そして上部のリボンからネットワークを選択します。
この状態で先程の検索結果画面を開くと、何やら怪しげな行が次々追加されていきます。
結論言うとこれらがページを開くために行われた全通信であり、この中にレース一覧データが隠されているはずです。
てことで、一行一行のレスポンスを確認しながらそれっぽいものを探します。
今回は1行目がレース情報のソースを受け取っているようです。
つまりrequestsでこの通信を再現してあげることで、データを取得できそうです。
#実装する
コーディングする前に先程見つけた通信を詳しく調べる為、Headersタブを開きます。
色々な項目がありますが、とりあえず今回はGeneralとForm Dataだけ見ればよいです。
Headers画面を見ていると以下のことがわかります。
- リクエスト先のURL:https://db.netkeiba.com/
- リクエスト方法:POST
- 検索条件:Form Dataの通り
Form Data部分を見るとなんとなくわかると思いますが、ここでページの表示内容を制御しています。
例えば"pid:race_list"は「レース情報を開く」ことをあらわすパラメータです。その下は設定した検索条件がつらつらと並んでいます。
ページを遷移してもURLが変わらないのは、表示内容をForm Data部分で制御していたからなのです。
纏めるとForm Dataに条件を指定し"https://db.netkeiba.com/" にPOST通信することで、欲しいレスポンスが得られるということです。
ということで、満を持してコーディングします。
とても単純で、requestsのparam引数に必要なForm Dataを設定してあげるだけです。
import requests
from bs4 import BeautifulSoup
#リクエスト先
url = "https://db.netkeiba.com/"
form_data = {"pid":"race_list",
"track[]": 1,
"jyo[]": "05",
"sort":"date",
"list" : 20,
"page": 1
}
#param引数にForm Dataを与えてpost通信
response = requests.post(url, params = form_data)
#BeautifulSoupで解析するだけ
soup = BeautifulSoup(response.content)
因みにデベロッパー画面にはありませんが、Form Dataの"page"パラメータを指定することで、検索結果の何ページ目の情報を取得することができます。
#終わりに
ログインを必要とするサイトからのスクレイピングでも応用できます。
目指せ脱Selenium。