業務で自動化テストでseleniumでサイトをスクレイピングするプログラムを組んでいたのですが、chromeのデベロッパツールで要素を確認してXPATHをコピーしてSeleniumの引数で指定した際に「no such element: Unable to locate element」が出ていて原因が分からずハマったので原因と解決方法をメモしておきます。
iframe(インラインフレーム)とは
ちなみにハマったのはこのサイトです
→ http://www1.river.go.jp/cgi-bin/SrchSiteSui2.exe
サイトの方を確認してもらえば分かりますが、print(driver.page_source)
でSeleniumが読み込んでいるソースを読み込むと以下のようなHTMLが読み込まれていました。
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=EUC-JP"> <meta http-equiv="Content-Style-Type" content="text/css"> <title>水系単位の観測所一括検索</title> </head> <frameset rows="96, *, 35" frameborder="NO"> <frame name="WSearch" id="WSearch" src="SrchSiteSui2.exe?MODE=1" scrolling="NO" marginwidth="1" marginheight="1" noresize=""> <frame name="SiteList" id="SiteList" src="SrchSiteSui2.exe?MODE=2" scrolling="YES" marginwidth="1" marginheight="1"> <frame name="TSearch" id="TSearch" src="SrchSiteSui2.exe?MODE=3" scrolling="NO" marginwidth="1" marginheight="1" noresize=""> <noframes> <BODY> <P>このページを表示するには、フレームをサポートしているブラウザが必要です。</P> </BODY> </noframes> </frameset> <font size="1"> </font></html>
どうやらこのページは古いサイトでインラインフレームが使われていたのが原因でした。要はこのインラインフレームというのはiframeタグでサイトのHTMLを分割していてxpathやcssがフレームごとに完全に独立しています。なのでseleniumでそのままデベロッパツールに表示されているxpathをコピーしてdriver.find_elements~をしてもその要素にアクセスすることができません。
フレームの確認
ウェブサイト内のフレームはBeautifulSoupを使えば以下のようにして確認できます。
~~~~ driver.get(対象サイトのurl) from bs4 import BeautifulSoup soup = BeautifulSoup(driver.page_source) print(soup.find_all('frame')) # もしくはsoup.find_all('iframe')
Seleniumでiframe内の要素にアクセスする
Seleniumでこういったインラインフレームで作られているサイトをスクレイピングする際は
<iframe>タグのname=で指定されているフレーム名を指定して以下のメソッドで都度フレームを切り替える必要があります。
# frameを切り替える(frame名はページ内のframeタグのname属性を指定) driver.switch_to.frame('フレーム名①')
対象のフレームにnameタグやidタグがない場合は以下のようにして確認&フレーム移動することも可能です。
# サイトの上から2番目のframeを選択して移動する iframe = driver.find_elements_by_tag_name('iframe')[1] driver.switch_to.frame(iframe)
他のフレームの指定方法(css_selectorやid指定など)については下記参照してください
参照:https://www.selenium.dev/ja/documentation/webdriver/browser/frames/
ちなみにこの処理はSeleniumの古いバージョンだとswitch_to_frame
というメソッドになっているのでそれで実行すると以下のようなエラーになってしまうので注意です。
Message='WebDriver' object has no attribute 'switch_to_frame'
またサイト内にフレームが複数あって切り替える場合は特定のフレーム内にいる状態から別のフレームの要素は見れないので、no such a element になってしまいます。なので以下のメソッドでデフォルトの要素に戻る必要があります。
# デフォルトのフレームに戻る driver.switch_to.default_content() # 別のフレームに切り替える driver.switch_to.frame('フレーム名②')
Seleniumのスクレイピングはまだまだ奥が深いです・・・
参考:https://stackoverflow.com/questions/70197944/webdriver-object-has-no-attribute-switch-to-frame
コメント
[…] 関連記事:【Python】Seleniumでiframe(インラインフレーム)にハマった話 […]