【Selenium】リンクを新しいタブで開いて移動【Python/ChromeDriver】

#ブラウザ操作してますか？
毎日同じブラウザ操作してませんか？

そんなブラウザ操作はSeleniumに任せましょう！

ブラウザを触っているときにリンクをCtrlを押しながらクリックして新しいタブで開くことありませんか？
僕はとてもよくやりますｗ

そんな一連の流れになります。

#リンクを新しいタブで開いて移動

```python:sample.py

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait

import platform
import sys

driver = webdriver.Chrome("chromedriver.exe") #chromedriver.exe読み込み

#
#
#該当ページまでの処理
#
#

#クリックする要素
element = driver.find_element_by_xpath('/html/body/hogehoge')
#element = driver.find_element_by_link_text("hogehoge") #リンクテキストで取得したい時はこちら

#クリック前のハンドルリスト
handles_befor = driver.window_handles

#(リンク)要素を新しいタブで開く
actions = ActionChains(driver)

if platform.system() == 'Darwin':
    #Macなのでコマンドキー
    actions.key_down(Keys.COMMAND)
else:
    #Mac以外なのでコントロールキー
    actions.key_down(Keys.CONTROL)

actions.click(element)
actions.perform()

#新しいタブが開ききるまで最大30秒待機
try:
    WebDriverWait(driver, 30).until(lambda a: len(a.window_handles) > len(handles_befor))
except TimeoutException:
    print('TimeoutException: 新規ウィンドウが開かずタイムアウトしました')
    sys.exit(1)

#クリック後のハンドルリスト
handles_after = driver.window_handles

#ハンドルリストの差分
handle_new = list(set(handles_after) - set(handles_befor))

#新しいタブに移動
driver.switch_to.window(handle_new[0])
```

負荷がかかりすぎないように気をつけて
適宜`wait`や`sleep`を入れましょうー

##解説
```python:sample.py
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
```
Seleniumライブラリと今回必要なパッケージを読み込みます。

```python:sample.py
import platform
import sys
```
OSを判断する為のライブラリを読み込みます。
エラーが発生した際に終了する為のライブラリを読み込みます。

```python:sample.py
driver = webdriver.Chrome("chromedriver.exe") #chromedriver.exe読み込み

#
#
#該当ページまでの処理
#
#
```
chromedriverを起動して操作しましょう。

```python:sample.py
#クリックする要素
element = driver.find_element_by_xpath('/html/body/hogehoge')
#element = driver.find_element_by_link_text("hogehoge") #リンクテキストで取得したい時はこちら
```
`xpath`や`link_text`でクリックする要素を変数に代入しましょう。

```python:sample.py
#クリック前のハンドルリスト
handles_befor = driver.window_handles
```
新しいタブが開く前のハンドルをリスト形式で取得します。

```python:sample.py
#(リンク)要素を新しいタブで開く
actions = ActionChains(driver)

if platform.system() == 'Darwin':
    #Macなのでコマンドキー
    actions.key_down(Keys.COMMAND)
else:
    #Mac以外なのでコントロールキー
    actions.key_down(Keys.CONTROL)

actions.click(element)
actions.perform()
```

`ActionChains`というクラスを使うとキー操作を記録して実行することができます。
Macとそれ以外のOSではGoogle Chromeブラウザの場合、新しいタブで開くショートカットキーが異なります。

Mac : command + クリック
WindowsやLinux : Ctrl + クリック

platformライブラリでOSを判断して条件分岐させています。

`actions.perform()`で記録したキー操作が実行されます。

```python:sample.py
#新しいタブが開ききるまで最大30秒待機
try:
    WebDriverWait(driver, 30).until(lambda a: len(a.window_handles) > len(handles_befor))
except TimeoutException:
    print('TimeoutException: 新規ウィンドウが開かずタイムアウトしました')
    sys.exit(1)

```
新しいタブが開いた時に時間がかかることがあるので待機させます。
`WebDriverWait(driver, 30)).until`ではdriverを最大30秒待機するようにします。


`lambda a: len(driver.window_handles) > len(handles_befor)`ではラムダ式を使用して一行で記載しています。

`len(handles_befor)`新しいタブで開く前のハンドル数より
`len(a.window_handles)`新しいタブが開いた瞬間のハンドル数が上回ったら待機が解除されます。

`try`を使ってエラーが出たらログにエラーを出力してシステムを終了しています。

```python:sample.py
#クリック後のハンドルリスト
handles_after = driver.window_handles
```
新しいタブが開いた後のハンドルをリスト形式で取得します。

```python:sample.py
#ハンドルリストの差分
handle_new = list(set(handles_after) - set(handles_befor))
```

リストを`set`に変換した後に`-`で引き算のようにすると差分を取得することができます。
それを変数に代入しています。

```python:sample.py
#新しいタブに移動
driver.switch_to.window(handle_new[0])
```

差分となった`handle_new[0]`が新しいタブのハンドルになるので`switch_to.window`で移動します。


#おまけ

タブを開きすぎて訳がわからなくなってもいつでも戻れるように
(そんなことにはならないようにしましょうｗ)

事前にカレントハンドルを取得しておくといいかもしれません。

```python:sample.py

#カレントウインドウ(タブ)のハンドルを代入
current_handle = driver.current_window_handle

#
#
#処理
#
#

#カレントウインドウ(タブ)に移動
driver.switch_to.window(current_handle)
```

ありがとうございました！