質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Firefox

Mozilla Foundationによって作られた無料、オープンソース、クロスプラットフォームなウェブブラウザ

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

Q&A

解決済

2回答

4059閲覧

「Selenium × Firefox × Python」で、ActionChainsモジュールで2回以上同じアクションを行う方法について

MT-333

総合スコア22

Firefox

Mozilla Foundationによって作られた無料、オープンソース、クロスプラットフォームなウェブブラウザ

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

0グッド

0クリップ

投稿2020/07/13 09:07

編集2020/07/13 09:09

前提・実現したいこと

「Selenium × Firefox × Python」の環境でブラウザ操作の自動化を行っています。

ActionChainsモジュールを使用した操作について、
2回以上同じ操作を行う際のコードの書き方をご教示ください。

【自動化したい処理について】
マウスのホバーでメニューが表示されるドロップダウンリストについて、
「リストにホバー → 任意のメニューを選択 → 表示された画面を閉じる」
という一連の操作を、ActionChainsモジュールのmove_to_element()メソッドを
使用して実現しようとしています。

【発生している事象】
該当のコードの20行目「actions.move_to_element(target_list).perform()」で
リストへのホバーが実行されず処理が停止する。

【期待値】
リストへのホバーが実行され、2回目以降の処理も停止せず実行されること

【備考】
Chromeで同様のコードを実行した場合は、問題なく最後まで処理が実行されました。

ご回答をよろしくお願いいたします。

該当のソースコード

Python

1from selenium import webdriver 2from selenium.webdriver.common.action_chains import ActionChains 3 4# ---------------------------------------------------------------------------------------------------- 5driver = webdriver.Firefox(executable_path = "ドライバーパス") 6 7driver.maximize_window() 8driver.get("サイトURL") 9 10 11## ホバーでメニューが表示されるドロップダウンリスト操作 12actions = ActionChains(driver) 13target_list = driver.find_element_by_xpath('リストのxpath') 14 15# 1回目 16actions.move_to_element(target_list).perform() 17driver.find_element_by_xpath("XXXX").click() # 任意のメニューを選択 18driver.find_element_by_xpath("XXXX").click() # 表示された画面を閉じる 19 20# 2回目 21actions.move_to_element(target_list).perform() 22driver.find_element_by_xpath("XXXX").click() # 任意のメニューを選択 23driver.find_element_by_xpath("XXXX").click() # 表示された画面を閉じる 24 25driver.quit() 26# ----------------------------------------------------------------------------------------------------

補足情報(FW/ツールのバージョンなど)

OS:Windows10 1909
言語:Python 3.7.3
ブラウザ:Firefox 78.0.2(64bit)
WEBドライバー:geckodriver.exe 0.26.0(64bit)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

自己解決したので方法を記載しておきます。

結論から言うと、ActionChainsモジュールのインスタンスを生成後、
reset_actions()メソッドを呼び出し、保存されているアクションを逐一
解放してあげることで、同じ処理を複数回行うことができるようになりました。

■参考:非公式リファレンス「Selenium Python Bindings 2」
https://kurozumi.github.io/selenium-python/api.html#module-selenium.webdriver.common.action_chains

例として、質問文内の「該当のソースコード」の該当処理部分を
以下のように変更することで、コードの最後まで実行が可能となりました。

Python

1## ホバーでメニューが表示されるドロップダウンリスト操作 2target_list = driver.find_element_by_xpath('リストのxpath') 3 4# 1回目 5actions = ActionChains(driver) 6actions.move_to_element(target_list).perform() 7driver.find_element_by_xpath("XXXX").click() # 任意のメニューを選択 8driver.find_element_by_xpath("XXXX").click() # 表示された画面を閉じる 9 10# 2回目 11actions = ActionChains(driver) 12actions.reset_actions() # キューに溜められたアクションを解放 13actions.move_to_element(target_list).perform() 14driver.find_element_by_xpath("XXXX").click() # 任意のメニューを選択 15driver.find_element_by_xpath("XXXX").click() # 表示された画面を閉じる

※ 1回目の実行前のリセットは必要ありません。
※ 各実行ごとにインスタンスの生成(actions = ActionChains())を入れないとエラーとなります。

投稿2020/08/12 02:28

編集2020/08/12 02:33
MT-333

総合スコア22

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

1回目が終わった後にもう一度

python

1actions = ActionChains(driver) 2target_list = driver.find_element_by_xpath('リストのxpath')

を入れてみてください。

コメントを受けて追記

だったらこれ試してみてください。

マウスのホバーとその後のクリックの代わりにこれをやるイメージです。

python

1driver.execute_script(''' 2function post(path, params, method='post') { 3 const form = document.createElement('form'); 4 form.method = method; 5 form.action = path; 6 7 for (const key in params) { 8 if (params.hasOwnProperty(key)) { 9 const hiddenField = document.createElement('input'); 10 hiddenField.type = 'hidden'; 11 hiddenField.name = key; 12 hiddenField.value = params[key]; 13 14 form.appendChild(hiddenField); 15 } 16 } 17 18 document.body.appendChild(form); 19 form.submit(); 20} 21post('/contact/', {name: 'Johnny Bravo'}); 22''')

DOM上に無理やりこのJSを出現させて実行するイメージです。
実際のところはGETリクエストになると思います。

投稿2020/07/14 12:31

編集2020/07/22 13:16
shirai

総合スコア1289

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

MT-333

2020/07/15 09:54 編集

ご回答ありがとうございます! ご指摘の通り、2回目の処理の直前に actions = ActionChains(driver) target_list = driver.find_element_by_xpath('リストのxpath') を入れてみましたが、同様に2回目の処理で止まってしまいました。 また、2回目のインスタンス名と要素名をaction2、target_list2のように変えて取得も 試してみましたが、こちらも同様に止まってしまいます。 引き続きよろしくお願いします。
shirai

2020/07/15 12:41

となると、私試したことないのでわからないですが、 firefoxとactionchainsの相性が悪いのかもしれません。 スクレイピングは往々にしてこういうことがよくあります。 ちなみに経験則的にはangularjsを使ったサイトとも非常に悪いです。 actions.move_to_element(target_list).perform() driver.find_element_by_xpath("XXXX").click() # 任意のメニューを選択 という2行を実行することで実際に裏で行われることは、 なんらかのJSを実行することだと思います。 どうしてもfirefoxでやりたいというのなら、 actionchainsを諦めてこのJSを直に実行させる方が早いと思います。 driver.execute_script() という関数があるので、中にJSを文字列で書き込んでやると実行されます。 driver.execute_script('document.getElementById("hoge").click()') のような感じで使えます。
MT-333

2020/07/17 02:25

>driver.execute_script() >という関数があるので、中にJSを文字列で書き込んでやると実行されます。 >driver.execute_script('document.getElementById("hoge").click()') >のような感じで使えます。 このような便利な関数があるのですね、初めて知りました。 ご教示いただきありがとうございます。 追加の質問で恐縮ですが、「要素にホバーする」という操作をJSではどのように 書けばよいでしょうか。調べているのですがなかなか見つからず... というのも、要素をクリックするだけであればPythonでもできるので、 もともとは target_list についても target_list.click() のように書いていたのですが、 スクリプトを動かすと何故かうまく動作せず、クリックではなくホバーだと うまく動作するので仕方なくActionChainsを採用していました。 よろしくお願いします。
MT-333

2020/07/17 07:31

Beginner_ABC さん ご回答ありがとうございます! 良く分かっておらずで申し訳ないのですが、 記載いただいたサイトでの addEventListener() で実現できる処理は、 「リストにマウスオーバーされたときに何らかのイベントを発火させる」メソッドかと思いますが、 「リストにマウスオーバー自体を行う」操作はJSでは難しいのでしょうか。。 (.click()でクリックを行うのと同じようなイメージです)
shirai

2020/07/18 03:34

「表示された画面」について伺いたいのですが、 1. スクレイピングではなく手動で、ホバーできる画面を開いたところで、パソコンのインターネット接続を切って、目的の要素をクリックした時に、その画面は表示されるでしょうか。 2. 表示された画面というのはページが遷移するわけではなく、ajaxで一部分が変わるものですか、それとも、ポップアップが表示されますか。
MT-333

2020/07/20 05:26

返信が遅くなりすみません。 1:ネットワーク接続を切った場合は表示されず、通信エラーとなります。  (APIリクエストを行っているため) 2:ポップアップ(ダイアログ)表示になります。 よろしくお願いします。
shirai

2020/07/22 13:16

追記しました
MT-333

2020/08/03 03:09

記載いただいたjsのコードですが、すみません、私があまりjsに明るくないので post()関数を呼び出す際にどのような引数を渡してあげればよいのかが良く分かっておりません。 内部処理としてはhiddenタイプのinput要素を生成し、それをinput→form→bodyと追加して 最後にsubmit()で送信しているという、なんとなくの流れは分かるのですが... また、色々試してみましたがそもそも405エラーがサーバから返ってくるので、 サーバ側で不正なpostリクエストは許可されていないのかもしれません。
shirai

2020/08/03 14:19

うーんなるほど。。。 2回目のactionsの宣言前にsleepを入れるとかでもないですよね。。。 こうなったらpyautoguiを使うしかなさそうですね。 ひとまずこのワードで検索してみていただけますか。 私も実際に使ったことがないのでよくは分かっていませんが、 DOMの状態などに影響は受けないはずです。
MT-333

2020/08/12 02:08

返信が遅くなり申し訳ありません。 そうですね、sleep()などによるwaitでは解消されませんでした。 また、内部の作り的にpyautoguiモジュールのキー操作で要素にフォーカスし、 何らかのキー操作を行っても反応しなかったので、こちらについても対処は難しそうです。 しかし、色々と調べていましたら自己解決できましたので、同事象で困っている他ユーザの ためにも別途記入しておきます。 shiraiさんには何度もご回答いただき大変助かりました。ありがとうございました。 また何かありましたらよろしくお願いいたします。
Reach

2020/08/12 03:59

release() って 駄目でしょうか?
MT-333

2020/08/12 05:47

「reset_actions()の代わりにrelease()で実現できるか?」という意味であれば、 私が試した限りではうまくいきませんでした。。 (引数の指定の仕方が間違っているだけかもしれませんが...)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問