【Python】要素がないだと… それ、iframeではありませんか?


背景

Pythonでスクレイピングをやってみようと思い、
取り合えず地図画像でも集めてみようかな…ということで、
中部電力が公開している地図画像をスクレイピング。

しかし、何度やっても要素を取得してくれない。

import time
from selenium import webdriver

#IEを起動
#ドライバーの保管ディレクトリを記述
driver = webdriver.Ie("C:\\Users\\oooo\\Desktop\\Python\\IEDriverServer.exe")

#パワーグリッドにアクセス
driver.get("https://powergrid.chuden.co.jp/kisyo/")
time.sleep(5)
element = driver.find_element_by_css_selector("#thunder-image-big")

print(element)

>>>NoSuchElementException: Message: Unable to find element with css selector == #thunder-image-big

検証ツールを開いて地図画像の要素にあるclassやidなど該当しそうな属性を
指定して取得を試みるが、「そんなものはねぇよ」と突っぱねられます。

本当に何が原因なのかわからなかったので、teratailで質問をしたら
解決したので備忘としてこの記事を書きました。

環境

使用ブラウザ:IE11
OS:Microsoft Windows 10 Home
エディタ:Jupyter notebook

こうやったらできた(結論)

import time
from selenium import webdriver
import urllib.request
from datetime import datetime

#IEを起動
#ドライバーの保管ディレクトリを記述
driver = webdriver.Ie("C:\\Users\\oooo\\Desktop\\Python\\IEDriverServer.exe")

#パワーグリッドにアクセス
driver.get("https://powergrid.chuden.co.jp/kisyo/")
iframe = driver.find_element_by_id('upper_main_left_cont')
driver.switch_to.frame(iframe)

time.sleep(5)
element = driver.find_element_by_css_selector("#thunder-image-big").get_attribute("src")

#ファイル名を日付で保存
now = datetime.now()
urllib.request.urlretrieve(element, now.strftime('%Y%m%d_%H%M%S') + '.png')

解説

自分もまだまだ、よちよちPythonマンなので復習用に書いておきます。

モジュールをインポート

import time                          #時刻に関するさまざまな関数を提供
from selenium import webdriver       #Webの自動テストのためのライブラリ
import urllib.request                #URL を開いて読むためのモジュール
from datetime import datetime        #日付や時刻を操作するためのクラスを提供

Pythonでライブラリの中のモジュールに含まれる
関数、クラスを利用するには、次のようにimportを記述し、
読み込んでから利用します。

import ライブラリ名モジュール名

準備

Seleniumでは、PythonのコードからWEBブラウザを操作します。
操作するためには、WebDriverというものが必要になります。

下記のリンクからドライバーをインストールします。
http://selenium-release.storage.googleapis.com/index.html?path=3.9.0/

あとは、ブラウザやレジストリの設定がありますので、
こちらを参考に進めていきました。

SeleniumでInternet Explorer11を動かす方法

ブラウザの指定

driver = webdriver.Ie("C:\\Users\\oooo\\Desktop\\Python\\IEDriverServer.exe")

このようにwebdriverのIEインスタンスを作成することによって
ブラウザを操作することが可能になります。
webdriverはPATHが通ったディレクトリに配置されていないと
実行に失敗してしまうので注意してください。

例として、デスクトップの"Python"というフォルダ内に置いているので
上記のようにPATHを指定しています。

ページを開く

Seleniumのget関数を使用して、指定したページのFullPATHを記述します。

driver.get("https://powergrid.chuden.co.jp/kisyo/")

要素の指定

通常なら、find_element_by_~という関数で、適した属性
(xpath, id, cssSelecter, classNameなど)から要素を取得します。

参考記事: Selenium Python Bindings
.

しかし今回、取得したい地図画像は"iframe"という形で扱われていました。

iframeとは

別のWebページや画像などをあたかもページの要素の一つのように埋め込んで
一体的に表示することができるものです。
表示する内容はsrc属性でURLの形で指定されます。

よくある公式サイトにYouTubeの動画を表示させているあれも
iframeで構成されています。

なので、スクレイピングで画像取得できていなかったのは、
今まで要素を取得しようとしていた属性は、iframeを入れるための器であって
中身は空っぽだったからです。(間違っていたらご指摘願います)

iframeを操作できるようにする

iframeは別ウィンドウとして扱われるので、
switch_to.frameを使ってiframe側に切り替える操作をします。

iframe = driver.find_element_by_id('upper_main_left_cont') #対象のインラインフレームIDを取得
driver.switch_to.frame(iframe)                             #取得したインラインフレームにスイッチ

要素を取得

前の操作で、iframeを切り替えて要素の取得が出来ます。

element = driver.find_element_by_css_selector("#thunder-image-big").get_attribute("src")

例として、cssセレクタを指定することで要素を取得しています。
他にもname属性やid属性などで指定が可能です。

参考記事:Seleniumクイックリファレンス

保存

保存するファイルのファイル名が被らないように、
要素を取得した時の日付をファイル名にして保存します。

now = datetime.now()   #現在時刻を取得
urllib.request.urlretrieve(element, now.strftime('%Y%m%d_%H%M%S') + '.png')

.

urlretrieveは、ファイルをダウンロードし保存する際に使用します。

urllib.request.urlretrieve(URL, 保存先のファイル名)

.

例として、URLにあたる場所にelementという変数を置いていますが、
前述でelementの中にはdriver.find_element…で取得してきたimgURLが既に入っています。

element = driver.find_element_by_css_selector("#thunder-image-big").get_attribute("src")
print(element)

>>>https://powergrid.chuden.co.jp/kisyo/Thunder_202011281715_1_0.png

.

now.strftimeは現在時刻を字列への変換するメソッドです。
('%Y%m%d_%H%M%S')という記述は、書式化コードで
"西暦""何月""何日""何時""何分""何秒"と表示させます。

さらに、拡張子を'.png'に指定して保存します。

上手に保存ができました。

参考記事