阿部寛をWebDriverでいじくる

# なにこれ
GoでWebDriverを使いたくて、試しに阿部寛のホームページでも回遊するかー。と軽いノリで手をつけたらちょいハマったのでメモ。
最終的に👇みたいなプログラムができた。

![あべひろし.gif](https://qiita-image-store.s3.amazonaws.com/0/181482/08289f1c-461b-a52f-6ccd-58c4f2314a81.gif)


# この記事読んで得られるモノ
- [阿部寛のホームページ](http://abehiroshi.la.coocan.jp/)で**メンズノンノ時代の阿部寛**のスクショを取れるようになる
- `frameset`および`frame`が利用されているwebページを操作できるようになる
- Go言語でブラウザ操作を行えるようになる


# 必要なもの
- [Go言語](https://golang.org/)
- [ChromeDriver](http://chromedriver.chromium.org/downloads)
- [Agouti](https://agouti.org/)

macなら以下でChromeDriverとAgoutiをインストールできます。簡単ね。

```sh
brew install chromedriver
go get github.com/sclevine/agouti
```

# 回遊する方法
以下コードで回遊できます。

```go
package main

import (
	"log"

	"github.com/sclevine/agouti"
)

func main() {
	driver := agouti.ChromeDriver()

	// headlessにする場合はこっちを使う
	// driver := agouti.ChromeDriver(
	// 	agouti.ChromeOptions("args", []string{"--headless", "--disable-gpu", "--no-sandbox"}),
	// )
	if err := driver.Start(); err != nil {
		log.Fatalf("driverの起動に失敗しました : %v", err)
	}
	defer driver.Stop()

	page, err := driver.NewPage(agouti.Browser("chrome"))
	if err != nil {
		log.Fatalf("セッション作成に失敗しました : %v", err)
	}

	// 阿部寛のウェブページに遷移する
	if err := page.Navigate("http://abehiroshi.la.coocan.jp/"); err != nil {
		log.Fatalf("阿部寛になにかあったかもしれません : %v", err)
	}

	// 写真集のリンクを検索する
	// framesetの中の要素を検索するには一旦該当のフレームにフォーカスしなければならない
	// 左側のフレームにフォーカスする
	if err := page.FindByXPath("/html/frameset/frame[1]").SwitchToFrame(); err != nil {
		log.Fatalf("阿部寛の左側frameにフォーカスできませんでした : %v", err)
	}
	// 「写真集」をクリック
	if err := page.FindByXPath("html/body/table/tbody/tr[10]/td[3]/p/a").Click(); err != nil {
		log.Fatalf("阿部寛の写真集が見つかりませんでした : %v", err)
	}

	// フレームのフォーカス外すためrootにもどる
	if err := page.SwitchToRootFrame(); err != nil {
		log.Fatalf("しょうも無いエラーが発生しました : %v", err)
	}

	// 右側のフレームにフォーカスする
	if err := page.FindByXPath("/html/frameset/frame[2]").SwitchToFrame(); err != nil {
		log.Fatalf("阿部寛の右側frameにフォーカスできませんでした : %v", err)
	}

	// 「メンズノンノ阿部寛」をクリック
	if err := page.FindByXPath("/html/body/table/tbody/tr[8]/td[2]/strong/a").Click(); err != nil {
		log.Fatalf("阿部寛のメンズノンノがみつかりませんでした : %v", err)
	}

	// スクショとる
	if err := page.Screenshot("./abe_hiroshi.jpg"); err != nil {
		log.Fatalf("スクショ取れまへん : %v", err)
	}
}
```

# ハマったこと
#### frame内部の要素にアクセスできないよぉぉぉ😭
コードで言うと`// 写真集のリンクを検索する`のコメント以下でハマってた。。。

トップページから**写真集**のリンクをクリックしようとGoogle ChromeのDeveloper Toolsで`XPath`をコピーして`FindByXPath`に貼り付けたのですが、要素が見つからないエラーが発生した。
>failed to select elements from selection 'XPath: html/body/table/tbody/tr[10]/td[3]/p/a [single]': element not found

なんで。。。

ちゃんとHTMLを読んで見ると`framesetおよびframe`の存在を見つけました。
<img width="715" alt="スクリーンショット 2019-03-30 17.21.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/181482/4d16cb41-b895-b532-20a6-5eac29d5366d.png">

あれ？Developer ToolsでコピーしたXPathは`html/body/table/tbody/tr[10]/td[3]/p/a`ですね。これじゃ見つからないですね。
`html/frameset/frame/.......`って書けば良いのかな？
ん？`#document`ってなにこれ？っていうかframeってなに？わからぬ。。。

...

どうやら阿部寛のホームページは以下のように2つのframeで構成されているということがわかった。
<img width="863" alt="スクリーンショット 2019-03-30 17.58.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/181482/0c791bdf-7488-029c-50c4-1986df9c329c.png">



#### 解決方法
AgoutiのドキュメントでFrameで検索してみると`SwitchToFrame()`ってメソッドを見つけた。
>func (s *Selection) SwitchToFrame() error
SwitchToFrame focuses on the frame specified by the selection. All new and existing selections will refer to the new frame. All further Page methods will apply to this frame as well.

https://godoc.org/github.com/sclevine/agouti#Selection.SwitchToFrame
どうやらframeにフォーカスを当てる必要があるらしい。
そのためframeを指定してから要素を検索すればOK。
またframeのフォーカスを外す` SwitchToRootFrame()`ってメソッドも用意されていた。

なので、以下方針でリンクをクリックすることにする

1. トップページにアクセス
2. 左側のフレームにフォーカスする
3. `写真集`リンクをクリックする
4. フレームのフォーカスをRootに戻す
5. 右側のフレームにフォーカスする
6. `メンズノンノ阿部寛`リンクをクリックする


```go
// 写真集のリンクを検索する
// frameの中の要素を検索するには一旦該当のフレームにフォーカスしなければならない
// 左側のフレームにフォーカスする
page.FindByXPath("/html/frameset/frame[1]").SwitchToFrame()

// 「写真集」をクリック
page.FindByXPath("html/body/table/tbody/tr[10]/td[3]/p/a").Click()

// 左側フレームのフォーカス外すためrootにもどる
page.SwitchToRootFrame()

// 右側のフレームにフォーカスする
page.FindByXPath("/html/frameset/frame[2]").SwitchToFrame()

// 「メンズノンノ阿部寛」をクリック
page.FindByXPath("/html/body/table/tbody/tr[8]/td[2]/strong/a").Click()
```

これでframeを利用しているWebページに回遊できるようになりました👍