jsを使うサイトをスクレイピングしたいと思って調べた結果、pythonを使う記事は日本語でたくさんあったが、goを使ったものはあまり見かけなかったのでメモを作成。
※以下は2018年6月下旬時点の情報
準備
agoutiのインストール
- 参考にさせていただきました→https://qiita.com/tenten0213/items/1f897ff8a64bd8b5270c
Web Driverのインストール
- Chrome用をインストール
$ brew install chromedriver
- selenium-server-standaloneもインストール
$ brew install selenium-server-standalone
agoutiのインストール
$ go get github.com/sclevine/agouti
###goqueryのインストール
- 参考にさせていただきました→https://qiita.com/Yaruki00/items/b50e346551690b158a79
$ go get github.com/PuerkitoBio/goquery
##Document
##前提
自分の場合は、ブラウザを動かして
1. ブラウザを操作して狙った情報を出す
2. その結果をクロールする
(3. 再び別の情報を出す)
の3段階をループしたかったので、以下のようなコードを書いた。
###1. ブラウザを操作して狙った情報を出す
サンプルコード
- ブラウザを起動して目的のページを開く
sample.go
url = "https://example.com" // urlを指定
driver := agouti.ChromeDriver() // ドライバの起動
err := driver.Start()
if err != nil {
log.Printf("Failed to start driver: %v", err)
}
defer driver.Stop()
page, err := driver.NewPage(agouti.Browser("chrome")) // クロームを起動。page型の返り値(セッション)を返す。
if err != nil {
log.Printf("Failed to open page: %v", err)
}
err = page.Navigate(url) // 指定したurlにアクセスする
if err != nil {
log.Printf("Failed to navigate: %v", err)
}
- 目的の場所をクリックして狙った情報を出す(例:セレクトボックスをクリックしてその中の選択肢のすべてを取る)
sample.go
curContentsDom, err := page.HTML()
if err != nil {
log.Printf("Failed to get html: %v", err)
}
readerCurContents := strings.NewReader(curContentsDom)
contentsDom, _ := goquery.NewDocumentFromReader(readerCurContents) // 現状ブラウザで開いているページのDOMを取得
listDom := contentsDom.Find(" selector ").Children() // selector 部分にセレクトボックスのセレクタを入れる。セレクトボックス内の子要素を全部取得
listLen := listDom.Length() // セレクトボックスの子要素の個数を取得
for i:= 1; i <= listLen ; i++ {
iStr := strconv.Itoa(i)
page.Find(" selector > option:nth-child(" + iStr + ")").Click() // セレクタの属性(ここではoption)のバリューで繰り返しクリックする
time.Sleep(2 * time.Second) //適宜ブラウザが反応するための間を取る
}
###2. その結果をクロールする
サンプルコード
- 以下のコードは上記のfor iteration内で実施
sample.go
curContentsDom, err := page.HTML()
if err != nil {
log.Printf("Failed to get html: %v", err)
}
readerCurContents := strings.NewReader(curContentsDom)
contentsDom, _ := goquery.NewDocumentFromReader(readerCurContents) // ページ内容が変化しているので再取得
contentsDom.Find(" selector ").Each(func(_ int, s *goquery.Selection) { // 繰り返し取得したい部分までのセレクタを入れる
data := s.Find(" selector ").Text() // テキスト要素を取得したい場合
link, _ := s.Find(" selector ").Attr("href") // アンカーリンク要素を取得したい場合
alt, _ := s.Find(" selector ").Attr("alt") // alt要素を取得したい場合
time.Sleep(1*time.Second) //サーバに負荷を与えないように。
})
###(3. 再び別の情報を出す)
- 1.内のforループ内で処理。
##よく使いそうなagoutiのメソッド
- 公式Documentから引用
method.go
func (s *Page) Find(selector string) *Selection // Page型のメソッド。セレクタを入れるとそのセレクタの箇所を特定して Selection型を返す
func (s *Selection) Click() error // Selection型のメソッド。引数のSelectionのすべての要素をクリックする。返り値のerrと、for文のcontinue処理を組み合わせて使える
func (s *Selection) DoubleClick() error // Selection型のメソッド。引数のSelectionのすべての要素をダブルクリックする。
func (p *Page) Refresh() error // Page型のメソッド。ページ全体をリロードする
- 私は使わなかったが、ブラウザの戻る、進む、マウス操作もできるので、一通りのブラウジングの再現はできそう。
##終わりに
英語Documentsしか自分は見つけられなかったので、agoutiを使う人の足しになればと思ってます。