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

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

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

```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など該当しそうな属性を
指定して取得を試みるが、「そんなものはねぇよ」と突っぱねられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/2401098f-14bf-5b8a-304f-f71e1fc1b4d7.png)

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

#環境
使用ブラウザ：IE11
OS：Microsoft Windows 10 Home
エディタ：Jupyter notebook

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

```python
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マンなので復習用に書いておきます。

##モジュールをインポート

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

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

```python
import ライブラリ名／モジュール名
```

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

下記のリンクからドライバーをインストールします。
http://selenium-release.storage.googleapis.com/index.html?path=3.9.0/
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/ee1467de-1a77-57f8-bfa8-3977d94af5e6.png)

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

:point_right:[SeleniumでInternet Explorer11を動かす方法](https://bitwave.showcase-tv.com/selenium%E3%81%A7internet-explorer11%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99%E6%96%B9%E6%B3%95/)

##ブラウザの指定

```python
driver = webdriver.Ie("C:\\Users\\oooo\\Desktop\\Python\\IEDriverServer.exe")
```
このようにwebdriverのIEインスタンスを作成することによって
ブラウザを操作することが可能になります。
webdriverはPATHが通ったディレクトリに配置されていないと
実行に失敗してしまうので注意してください。

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

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

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

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

[参考記事: Selenium Python Bindings](https://kurozumi.github.io/selenium-python/locating-elements.html)
.

しかし今回、取得したい地図画像は"iframe"という形で扱われていました。
![キャプチャ6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/2a88ae3c-a2f7-6042-0732-aa0950a0bcca.png)

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

![プレゼンテーション1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/fa9388fd-3e9c-d984-0f31-833a86a724a5.jpeg)

よくある公式サイトにYouTubeの動画を表示させているあれも
iframeで構成されています。
![8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/469ec1c3-a043-66b6-7b74-e796bab7764a.png)

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

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

```python
iframe = driver.find_element_by_id('upper_main_left_cont') #対象のインラインフレームIDを取得
driver.switch_to.frame(iframe)                             #取得したインラインフレームにスイッチ
```
##要素を取得
前の操作で、iframeを切り替えて要素の取得が出来ます。

```python
element = driver.find_element_by_css_selector("#thunder-image-big").get_attribute("src")
```
例として、cssセレクタを指定することで要素を取得しています。
他にもname属性やid属性などで指定が可能です。

[参考記事:Seleniumクイックリファレンス](https://www.seleniumqref.com/api/python/element_get/Python_find_element_by_css_selector.html)

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

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

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

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

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

```python
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'に指定して保存します。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/9462b7b8-82c2-c4df-f7d0-b258d7dd3675.png)

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

#参考記事

- [Seleniumで画面遷移した後、要素が取得できない](https://teratail.com/questions/306475#reply-429925)
- [【要素が無いと思ったら】Seleniumでiframeの内容を操作する。](https://python-auto.com/2019/12/25/%E3%80%90%E8%A6%81%E7%B4%A0%E3%81%8C%E7%84%A1%E3%81%84%E3%81%A8%E6%80%9D%E3%81%A3%E3%81%9F%E3%82%89%E3%80%91selenium%E3%81%A7iframe%E3%81%AE%E5%86%85%E5%AE%B9%E3%82%92%E6%93%8D%E4%BD%9C%E3%81%99/)
- [Pythonのdatetimeで日付や時間と文字列を変換（strftime, strptime）](https://note.nkmk.me/python-datetime-usage/)