概要
スクロールしないと続きのコンテンツが表示されないサイトに対してスクレイピングを行う方法。
よく見る無限スクロール方法だと取得できない(効率が悪い)場合に直面したので記載する。
よくある無限スクロール方法
「Selenium 無限スクロール」とかでググるとたくさん出てくる
https://qiita.com/zeeksan/items/f26519cbd529f5b09cbf。
https://www.sukerou.com/2021/09/pythonselenium.html
画面を下までスクロールさせることで続きのコンテンツが表示されるので
大体が以下の流れでスクロールして表示されている情報を一括で取得している。
- スクロール前のページの高さを取得
- ページ最下部まで、徐々にスクロールしていく(サンプルでは80%)
- 新しいコンテンツが読み込まれるのでスクロール後のページの高さを取得する
- スクロール前後でページの高さに変化がなくなったら無限スクロール終了とみなしてループを抜ける
url = "https://xxxxxxxxxxxxxxxxxxxxxxxxx/"
#ブラウザのウインドウ高を取得する
win_height = driver.execute_script("return window.innerHeight")
#スクロール開始位置の初期値(ページの先頭からスクロールを開始する)
last_top = 1
#ページの最下部までスクロールする無限ループ
while True:
#スクロール前のページの高さを取得
last_height = driver.execute_script("return document.body.scrollHeight")
#スクロール開始位置を設定
top = last_top
#ページ最下部まで、徐々にスクロールしていく
while top < last_height:
top += int(win_height * 0.8)
driver.execute_script("window.scrollTo(0, %d)" % top)
time.sleep(0.5)
#1秒待って、スクロール後のページの高さを取得する
time.sleep(1)
new_last_height = driver.execute_script("return document.body.scrollHeight")
#スクロール前後でページの高さに変化がなくなったら無限スクロール終了とみなしてループを抜ける
if last_height == new_last_height:
break
#次のループのスクロール開始位置を設定
last_top = last_height
この方法では、スクロールする度に情報が追加されてすべての情報が表示されている場合には対応できる。
しかし、情報が置換されて情報が入れ替わる(すべての情報が一括では出力されない)場合には対応できない。
スクロールの幅を狭めて少しずつスクロールしてもよいが、スクロール幅はウィンドウサイズ由来なので適切な幅はわからない。
効率も悪い。
対策
指定の要素の位置までスクロールする方法で対応できる。
JavaScriptでは以下のように指定要素までのスクロールを行える。
https://www.softel.co.jp/blogs/tech/archives/3824
document.getElementById("target").scrollIntoView(true)
現在表示されているコンテンツの最終位置まで一気にスクロールして、また新しいコンテンツを読み込み…
とやっていく方が画面サイズに依存せず効率が良い。
表示コンテンツの最終位置までスクロールするサンプル
nowItems = driver.execute_script("return document.getElementsByClassName('ListView__ItemContainer-sc-1veaxzq-1')")
driver.execute_script("document.getElementsByClassName('ListView__ItemContainer-sc-1veaxzq-1')[%d].scrollIntoView(true)" % int(len(nowItems)-1))
※ただしこのようなサイトは表示されているコンテンツが重複することが予想されるので、重複を防ぐために別途対応する必要がある。