【PythonでWEBスクレイピング】JavaScriptで表示される情報を取得する(selenium)

目次
本日の目標
こちらは、先日わたしが作成したルービックキューブのタイマーです。下の図の赤線で囲った部分に書いてある文字列を今回は取得してみようと思います。(以下、スクランブル文字列と呼びます)

前回までの要領で取得
早速ですが、前回までの要領で取得してみます。
from bs4 import BeautifulSoup import requests headers = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36' } url="https://cube.supisupi.com/" response = requests.get(url, headers=headers) soup = BeautifulSoup(response.text, "html.parser") scrambles = soup.select("#scramble") print(scrambles[0].text) #print(scrambles[0])
何も取得できませんでした。print文の”.text”を外して、要素ごと取得してみます。(コメントアウトしているprint文)
<div class="sm:flex sm:justify-center text-4xl" id="scramble"> </div>
要素は取得できてはいるものの、中身が空です。
値が取得できない原因
値が取得できない原因は、文字列の表示方法にあります。この文字列は、最初にページが表示された際にはまだ表示されておらず、load後にJavaScriptで表示処理を行っているところです。
さて、PythonのスクリプトでWEBページのHTMLを取得した際に、ページを構成しているCSSやJavaScriptの情報は取得できたとしても動作することはありません。ブラウザに読み込まれて初めて、CSSやJavaScriptは動作するのです。それ故、JavaScriptが動作しないので、スクランブル文字列は表示されていないのです。
ではどうすれば取得できるのか
ブラウザから閲覧しないと欲しい値が表示されません。それなら、Pythonスクリプトからの実行であっても、ブラウザを経由して情報を取得してしまえば良いではないかと。
Seleniumで情報を取得する
seleniumインストール
pip install selenium
前回までと合わせて、「beautifulsoup4」「requests」「selenium」がインストールされていればOK。
chromedriverインストール
https://googlechromelabs.github.io/chrome-for-testing
ここからdriverをダウンロードします。わたしの場合は、mac-arm64版。URLをブラウザに入力してあげれば問題なくダウンロードできると思います。
ダウンロードしたファイルを解凍して、お好きな場所に設置します。今回はここにしました。
/Users/supilog/tools/chromedriver-mac-arm64/
ディレクトリ配下にドライバがあるので、ドライバへのパスは以下の通り。
/Users/supilog/tools/chromedriver-mac-arm64/chromedriver
スクレイピング
driverを使っているので、書き方が前回までと多少違います。
from bs4 import BeautifulSoup import requests from selenium import webdriver from selenium.webdriver.chrome.service import Service chromedriver_path = '/Users/supilog/tools/chromedriver-mac-arm64/chromedriver' options = webdriver.ChromeOptions() options.add_argument('--headless') service = Service(executable_path=chromedriver_path) driver = webdriver.Chrome(service=service,options=options) url="https://cube.supisupi.com/" driver.get(url) html = driver.page_source soup = BeautifulSoup(html, "html.parser") titles = soup.select('#scramble')[0] print(titles.text)
【9行目】options.add_argument(‘–headless’)
これは、ヘッドレスブラウザを使用する宣言です。ヘッドレスブラウザというのは、GUI(グラフィカルユーザーインターフェイス)のないブラウザで、スクリプトから動作させるのによく用います。
【11行目】
ブラウザを起動。
気になる取得結果は
R2 D B U2 F2 U' B' D2 R2 F U B2 L' D2 U B R' U2 F' L
このスクランブル文字列は、load時にランダム生成しているので、数回実行してみる。
L' R F2 U' R D' F B2 D' R2 L' D2 U L F2 U2 L2 F2 D2 R2 B' R D2 U' B' D' B' L' F' L' F2 D2 R' F' U D R' U D F D U2 R' U B' D' F2 R2 U2 F L' U2 D L F L U F U' B' L U R2 D2
無事に毎回異なるスクランブル文字列が取得できました。
ブラウザの起動は時間がかかるよ
前回までと違って、今回はヘッドレスブラウザを利用した情報の取得を行いました。とても便利ですが、ブラウザを起動させている分、処理に時間がかかります。どのくらいの時間差があるか、少しやってみる。本ブログのタイトルを取得してみよう。
ブラウザを使わない場合
from bs4 import BeautifulSoup import requests import time start = time.time() headers = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36' } for i in range(10): url="https://supilog.supisupi.com/" response = requests.get(url, headers=headers) soup = BeautifulSoup(response.text, "html.parser") scrambles = soup.select(".header-title") print(scrambles[0].text) end = time.time() print(end - start)
3.132404088973999
ブラウザを使う場合
from bs4 import BeautifulSoup import requests import time from selenium import webdriver from selenium.webdriver.chrome.service import Service start = time.time() chromedriver_path = '/Users/supilog/tools/chromedriver-mac-arm64/chromedriver' options = webdriver.ChromeOptions() options.add_argument('--headless') service = Service(executable_path=chromedriver_path) driver = webdriver.Chrome(service=service,options=options) for i in range(10): url="https://supilog.supisupi.com/" driver.get(url) html = driver.page_source soup = BeautifulSoup(html, "html.parser") scrambles = soup.select(".header-title") print(scrambles[0].text) end = time.time() print(end - start)
6.405471086502075
2倍程度の時間がかかった。挙動を見ていると、単純にChromeの起動に時間がかかっている。今回は起動処理を1回しか行っていないが、何度も起動するような実装をしてしまうとかかる時間も膨大になるので注意だ。
まとめ
seleniumを利用したスクレイピングを実行してみました。いかがでしたか。
今回は運良く、起動して取得したら値がとれましたが、JavaScriptの処理が遅くて取得できない場合には、待機処理が必要になるケースもありそう(な予感)。sleepとかimplicitly_waitなどを利用することになるのでしょうか。それはまた別のおはなし。
閲覧していただき、ありがとうございました。ではまた。