【Golang】Collyを使用してWebスクレイピングを行う

概要

CollyはGo言語でWebスクレイピングを行うためのライブラリです。
https://github.com/gocolly/colly

以下にCollyの使い方を紹介します。

インストール

go modコマンドでgo.modファイルを作成し、go getでcollyを導入します。

1go mod init colly-scraping-test
2go get github.com/gocolly/colly/v2

collyの基本的な使用方法

Collyを使用してスクレイピングを行うには以下のような流れで処理を行います。

例として、このブログのトップページのプロフィール部分のプロフィール画像URLとプロフィール名を取得してみます。

 1package main
 2
 3import (
 4	"fmt"
 5
 6	"github.com/gocolly/colly/v2"
 7)
 8
 9func main() {
10	c := colly.NewCollector()
11
12	var profileImageURL string
13	c.OnHTML(".author_header > img", func(e *colly.HTMLElement) {
14		profileImageURL = e.Attr("src")
15	})
16
17	var profileName string
18	c.OnHTML(".author_header > h2", func(e *colly.HTMLElement) {
19		profileName = e.Text
20	})
21
22	err := c.Visit("https://kazusa-pg.com/")
23	if err != nil {
24		panic(err)
25	}
26
27	fmt.Println(profileImageURL)
28	fmt.Println(profileName)
29}

このコードは実行するとプロフィール画像のURLとプロフィール名が表示されます。

1https://kazusa-pg.com/images/profile-picture1.png
2かずさプログラマー

collectorのOnHTMLメソッドにはセレクター要素ごとに動作を定義できるので今回のコードでは以下の2つの動作を設定しています。

  • .author_header > img のセレクター要素でsrc属性をprofileImageURL変数に格納する
  • .author_header > h2 のセレクター要素のテキストをprofileName変数に格納する

リンク先のページの情報を取得する

Collyを使用すると指定したページに表示されている各リンクをたどって、リンク先のページの情報を取得することができます。
このブログのトップページに表示されている最新の記事10件にアクセスして、各記事のタイトルと何分で読了できるかの情報を取得してみます。

 1package main
 2
 3import (
 4	"fmt"
 5
 6	"github.com/gocolly/colly/v2"
 7)
 8
 9// 参考URL:https://github.com/gocolly/colly/blob/master/_examples/coursera_courses/coursera_courses.go
10
11type pageDetail struct {
12	title    string
13	readTime string
14}
15
16func main() {
17	pageDetails := []pageDetail{}
18
19	c := colly.NewCollector(
20		colly.AllowedDomains("kazusa-pg.com"),
21	)
22
23	detailCollector := c.Clone()
24
25	c.OnHTML(".excerpt_header > h3 > a", func(e *colly.HTMLElement) {
26		detailCollector.Visit(e.Attr("href"))
27	})
28
29	detailCollector.OnRequest(func(r *colly.Request) {
30		fmt.Println("visiting", r.URL.String())
31	})
32
33	detailCollector.OnHTML(".post_content", func(e *colly.HTMLElement) {
34		pd := pageDetail{
35			title:    e.ChildText(".post_title"),
36			readTime: e.ChildText(".post_time"),
37		}
38		pageDetails = append(pageDetails, pd)
39	})
40
41	c.Visit("https://kazusa-pg.com/")
42
43	for _, pd := range pageDetails {
44		fmt.Printf("%s %s\n", pd.title, pd.readTime)
45	}
46}

コードを実行すると各ブログのURLにアクセスし、リンク先のタイトルと何分で読了できるかの情報を表示します。

 1visiting https://kazusa-pg.com/wp-to-hugo/
 2visiting https://kazusa-pg.com/golang-get-files-in-zip/
 3visiting https://kazusa-pg.com/vba-get-files-in-zip/
 4visiting https://kazusa-pg.com/golang-colly-web-scraping/
 5visiting https://kazusa-pg.com/vba-picinfo/
 6visiting https://kazusa-pg.com/go-screen-capture/
 7visiting https://kazusa-pg.com/golang-robotgo/
 8visiting https://kazusa-pg.com/golang-stdin-unittest/
 9visiting https://kazusa-pg.com/go-mouse-mover/
10visiting https://kazusa-pg.com/golang-lifegame/
11WordPressからHugoに移行しました · 9 分で読了
12【Golang】zip内のファイル一覧を展開せずに取得する · 2 分で読了
13【VBA】zip内のファイル一覧を展開せずに取得する · 2 分で読了
14【Golang】Collyを使用してWebスクレイピングを行う · 5 分で読了
15【VBA】画像の幅と高さを取得する · 2 分で読了
16Robotgoで指定範囲のキャプチャーを連続で取得する · 1 分で読了
17Robotgoで自動操作 · 3 分で読了
18Golangで標準入力を使用する関数のテスト · 3 分で読了
19Robotgoでマウスポインタを自動で移動させる · 2 分で読了
20Golangでライフゲームを作成しました · 1 分で読了

ファイルや画像を保存する

Collyを使って画像をダウンロードして保存するにはcollectorのOnResponseメソッドを使用します。

OnResponseはサーバーからレスポンスを受け取るたびに呼び出されます。
レスポンスにはサーバーから返されたHTMLや画像などのコンテンツが含まれているのでSaveメソッドを使用することで
レスポンスの内容を保存することができます。

下記のコードは、このブログのトップページのプロフィール画像を保存するサンプルです。

 1package main
 2
 3import (
 4	"fmt"
 5
 6	"github.com/gocolly/colly/v2"
 7)
 8
 9func main() {
10
11	c := colly.NewCollector()
12	c.OnResponse(func(r *colly.Response) {
13		r.Save("./profile.png")
14		fmt.Print("保存しました")
15	})
16
17	err := c.Visit("https://kazusa-pg.com/images/profile-picture1.png")
18	if err != nil {
19		panic(err)
20	}
21
22}

コードを実行すると、コードを実行したパスに、このブログのプロフィール画像をprofile.pngとして保存します。

エラーを処理する

Collyを使ってWebスクレイピングを行う際にエラーが発生することがあります。
例えばサーバーから404エラーが返されたり、タイムアウトしたりすることがあります。

Collyではエラーを処理するためにOnErrorメソッドが用意されています。
このメソッドを設定することで、エラーが発生したときの動作を定義することができます。

下記のコードではテストサーバーから404 Not Foundが返ってくるのでOnErrorメソッドを使ってエラーを処理しています。

 1package main
 2
 3import (
 4	"fmt"
 5	"net/http"
 6	"net/http/httptest"
 7
 8	"github.com/gocolly/colly/v2"
 9)
10
11func main() {
12	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13		w.WriteHeader(http.StatusNotFound)
14	}))
15	defer server.Close()
16
17	c := colly.NewCollector()
18
19	c.OnError(func(r *colly.Response, err error) {
20		fmt.Println("エラーが発生しました:", err)
21	})
22
23	c.Visit(server.URL)
24}

コードを実行すると下記のように表示されます。

1エラーが発生しました: Not Found

もしくはVisitメソッドがerrorを返すので、errorの内容を元に処理を行います。

1	err := c.Visit("URL")
2	if err != nil {
3		panic(err)
4	}

参考リンク

Collyのサンプルコード
Collyのドキュメント
GoDoc

関連ページ