はじめに
JavaScriptで動的に生成されるWebページの場合、goqueryに直接URL渡しても正しい値でスクレイピングができない。
そのため、WebDriverを使ってブラウザに表示された実際のHTMLをgoqueryに渡してスクレイピングする必要がある。
環境
- Ubuntu 16.04.3
- go 1.8.3
インストール
- ChromeDriver
# インストール
export WD_VER=2.32
wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/${WD_VER}/chromedriver_linux64.zip
sudo unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/
sudo chmod +x /usr/local/bin/chromedriver
# 確認
$ which chromedriver
/usr/local/bin/chromedriver
- Go パッケージ
go get github.com/PuerkitoBio/goquery
go get github.com/sclevine/agouti
流れ
ブラウザ経由でHTMLを取得
WebDriverを使用してHTMLを取得する。
url := "http://target.example.com"
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"))
if err != nil {
log.Printf("Failed to open page: %v", err)
}
err = page.Navigate(url)
if err != nil {
log.Printf("Failed to navigate: %v", err)
}
// contentにHTMLが入る
content, err := page.HTML()
if err != nil {
log.Printf("Failed to get html: %v", err)
}
スクレイピング
goqueryを使用してスクレイピングする。
bodyタグのaタグからhrefとコンテンツを取り出している。
- Find()の指定方法
- body, .container, body[class="container"]
reader := strings.NewReader(content)
doc, _ := goquery.NewDocumentFromReader(reader)
doc.Find("body a").Each(func(i int, s *goquery.Selection) {
// bodyにコンテンツ, hrefにhrefの値が入る
body := s.Text()
href, _ := s.Attr("href")
fmt.Printf("body: %s, href: %s\n", body, href)
})
実践
ブラウザで表示し、クリックしてからHTMLを取得する
クリックすると表示内容が変化する際に変化後のコンテンツをスクレイピングしたい時、ブラウザでクリック操作をした後にHTMLを取得する。
package main
import (
"fmt"
"log"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/sclevine/agouti"
)
func main() {
url := "http://target.example.com"
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"))
if err != nil {
log.Printf("Failed to open page: %v", err)
}
err = page.Navigate(url)
if err != nil {
log.Printf("Failed to navigate: %v", err)
}
page.FindByButton("Next").Click()
content, err := page.HTML()
if err != nil {
log.Printf("Failed to get html: %v", err)
}
reader := strings.NewReader(content)
doc, _ := goquery.NewDocumentFromReader(reader)
doc.Find("body a").Each(func(i int, s *goquery.Selection) {
body := s.Text()
href, _ := s.Attr("href")
fmt.Printf("body: %s, href: %s\n", body, href)
})
}
まとめ
動的HTMLを取得し、スクレイピングを行うことができた。
ただ、WebDriverは、ブラウザを開いて操作がされるのでただスクレイピングしたいだけだと無駄な感じがあって、実際時間がかかる。