Selenium Web DriverのPythonバインディングを使ってブラウザを操作する

この記事は [Selenium/Appium Advent Calendar 2014](http://qiita.com/advent-calendar/2014/selenium) の11日目の記事です。
昨日の記事は @totutiteta さんの[Robot drives on the web: Robot Framework & Selenium2Library](http://qiita.com/totutiteta/items/e7b99a76231dbfb0aaf1) でした。

今日はSeleniumのPythonバインディングのライブラリ [selenium](https://pypi.python.org/pypi/selenium) を使ってFirefoxを操作する方法をまとめて見ました。 またseleniumを使って操作するためのライブラリを作ってみました。

# SeleniumのPythonバインディングのライブラリ [selenium](https://pypi.python.org/pypi/selenium) でFirefoxを操作してみる

この記事を書いた時点での [selenium](https://pypi.python.org/pypi/selenium) のversionは 2.44.0 が最新の用です。(おそらく2.35未満のversionでは以下のcodeは動作しないと思います)

## 起動
まずはブラウザを起動してみます。
今回はFirefoxを対象にしています。

### profileの作成
```py
from selenium.webdriver import FirefoxProfile

default_profile = {
    'security.warn_entering_secure': False,
    'security.warn_entering_secure.show_once': True,
    'security.warn_entering_weak': False,
    'security.warn_entering_weak._show_once': True,
    'security.warn_leaving_secure': False,
    'security.warn_leaving_secure.show_once': True,
    'security.warn_leaving_weak': False,
    'security.warn_leaving_weak._show_once': True,
    'security.warn_submit_insecure': False,
    'security.warn_viewing_mixed': False,
    'security.warn_viewing_mixed.show_once': True,
    }
profile = FirefoxProfile()
for name, value in default_profile.items():
    profile.set_preference(name, value)
```

### Proxyの設定の作成
Proxyオブジェクトはftp_proxy, ssl_proxy, http_proxy属性で設定します。
使わない時はNoneで良さそうです。

```py
from selenium.webdriver import Proxy
proxy = Proxy()
proxy.ftp_proxy = proxy.ssl_proxy = proxy.http_proxy = None
```

### Firefox起動

```py
from selenium.webdriver import Firefox
browser = Firefox(firefox_profile=profile, proxy=proxy)
browser.implicitly_wait = 10  # pageをloadするまでの待ち時間を設定
browser.delete_allcookies()  # Cookieを全消し
```

## 操作
では起動したブラウザを操作してみます。

### URLを指定してページを遷移させてみる

```py
url = 'http://qiita.com/'
browser.get(url)
```

### id属性を指定して要素を取得

```py
tag = browser.find_element_by_id('elemet-id')
```

### class属性を指定して要素のリストを取得

```py
tags = browser.find_elements_by_class_name('class-name')
```

### CSSセレクターを指定して要素のリストを取得
以下ではinputタグのリストを取得します。

```py
tags = browser.find_elements_by_css_selector('input')
```

### 要素をクリックする

```py
tag.click()
```

### Formのsubmitボタンでsubmitする

```py
form = browser.find_elements_by_tag_name('form')[0]
for tag in form.find_elements_by_tag_name('input'):
    type_ = tag.get_attribute('type')
    if type_ == 'submit':
        entry.submit()
```

### テキストボックスに文字を入力

```py
textboxes = [tag for tag in browser.find_elements_by_tag_name('input') if tag.get_attribute('type') == 'text']
textbox = textboxes[0]

textbox.send_keys('CHARACTOR')
```

### BackSpaceのキーコードを送ると文字を消せる

```py
from selenium.webdriver.common.keys import Keys
textbox.send_keys(Keys.BACK_SPACE)  # 1文字削除
textbox.send_keys(Keys.BACK_SPACE * 10)  # 10文字削除

```

### Windowの最大化

```py
browser.maximize_window()
```

### 要素が表示されている状態かどうかを確認する

```py
if tag.is_displayed():
    print(u'表示されている')
else:
    print(u'表示されていない')
```

### javascriptを実行する
以下では画面を下方向にスクロールしています。

```py
browser.execute_script('window.scrollTo(0, 1000)')
```

# seleniumを使って操作するためのライブラリを作ってみた
いろいろ操作はできるんですが実際に上記を使う為にはいろいろ遷移を入れたくなることが多いと思います。現在どのページにいるから何を実行するとか、ページの状態とか、以前いたページの状態を保持しておきたいケースもあるかもしれません。(実際にそういう局面に遭遇した)

なので、ちょっとフレームワークっぽく使えるライブラリを作ってみました。

**[pywad](https://pypi.python.org/pypi/pywad)**

[pywad](https://pypi.python.org/pypi/pywad)はざっと説明すると起動とか制御とかを定型的に記述できるようにしたライブラリです。

## インストール
Seleniumを使うのでSeleniumをインストールしておく必要があります。

```shell
$ pip install pywad
```

## 使い方

pywadにはRunnerクラスとPartクラスがあります。
Partクラスは1ページなど小さな制御単位の制御を記述する為のクラスで、Runnerクラスのパーツとなって動作します。

### Partクラス

Partクラスにはis_target()メソッドとrun()メソッドがあります。
is_target()は制御対象かどうかを判定するメソッドでこのメソッドがTrueを返した場合にrun()メソッドが実行されます。実際に使うときはis_target()とrun()をオーバーライドして使う事になります。2つのメソッドともにbrowserオブジェクトとステータスを保持する為のオブジェクトを渡されるのでそれを使って処理を定義します。

```py
from pywad.part import Part
from pywad.decorator import url_match

class GoogleTop(Part):
    def _is_search_button(self, text):
        for word in self.search_words:
            if word in text:
                return True

    @url_match('www\.google\.')
    def is_target(self, browser, status):
        return True

    def run(self, browser, status):
        entries = browser.find_elements_by_css_selector('input')
        for entry in entries:
            if entry.get_attribute('type') == 'text':
                entry.send_keys('test\n\n')
```

### Runnerクラス
RunnerクラスはbrowserやPart間にまたがる必要のあるデータ等を管理します。
RunnerクラスにPartクラスを登録することで対象のPartを探して実行します。

```py
from pywad.runner import Runner
def main():
    url = 'http://www.google.com'
    runner = Runner()
    runner.append(GoogleTop())
    runner.run(url)
```

### こんな感じで記述できます。

```py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from pywad.runner import Runner
from pywad.part import Part
from pywad.decorator import url_match

class GoogleTop(Part):
    def _is_search_button(self, text):
        for word in self.search_words:
            if word in text:
                return True

    @url_match('www\.google\.')
    def is_target(self, browser, status):
        return True

    def run(self, browser, status):
        entries = browser.find_elements_by_css_selector('input')
        for entry in entries:
            if entry.get_attribute('type') == 'text':
                entry.send_keys('test\n\n')

def main():
    url = 'http://www.google.com'
    runner = Runner()
    runner.append(GoogleTop())
    runner.run(url)

if __name__ == '__main__':
    main()
```

### 良ければ使ってみてください

# 明日は?

明日は、あれ、誰だろう。誰も登録してない.oO(誰か書いてほしいな)

明日は *** です。って言えないのでアドベントカレンダーのリンクを貼っておきます。
http://qiita.com/advent-calendar/2014/selenium

ではではヾ(･д･)Byёヾ(･д･)Byё