Goのスクレイピングライブラリ goquery と colly を試してみる

19616 ワード

概要

Goのスクレイピングライブラリでスター数が多い、goquerycollyを触ってみようと思います。
スクレイピングするのは、https://zenn.dev/ さんです。またこちらの記事ではCSVなど出力までは行っておりませんので悪しからず。

少しライブラリ調べてみた

goのスクレイピングライブラリを使用するにあたり、どんなものがあるのか調べてみました。
※ 2022/04/29時点での情報

  • goquery (スター数11.4k)
  • colly (スター数16.5k)
  • soup (スター数1.8k)
  • scrape (スター数1.5k)
    • メンテナンスされていなそう?

注意点

スクレイピングするにあたり、【2020年度版】個人用クローラーの開発手順とその注意点 -Qiitaを参考にさせていただき、robots.txtを確認しました。

zenn.devさんのrobot.txtはこちらでした。
自分も拝見し、今回Disallowに載っていなさそうな部分をスクレイピングさせていただきました。

User-agent: *
Disallow: /dashboard/
Disallow: /settings/
Disallow: /search?q=
Disallow: /report
Disallow: /onboarding

User-agent: Yahoo Pipes 1.0
Disallow: /

User-agent: 008
Disallow: /

User-agent: voltron
Disallow: /

User-agent: Bytespider
Disallow: /

User-agent: Livelapbot
Disallow: /

User-agent: Megalodon
Disallow: /

User-agent: ia_archiver
Disallow: /

Sitemap: https://zenn.dev/sitemap/sitemap.xml

使用例

※ Playground内では動かないので注意

goquery

goqueryは、Go標準のnet/htmlパッケージとCSSセレクタライブラリのcascadiaがベースになっており、jQueryっぽく書くことができます。

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/PuerkitoBio/goquery"
)

func main() {
	webPage := ("https://example.com/")
	resp, err := http.Get(webPage)
	if err != nil {
		log.Printf("failed to get html: %s", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		log.Fatalf("failed to fetch data: %d %s", resp.StatusCode, resp.Status)
	}

	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		log.Printf("failed to load html: %s", err)
	}

	// ここでタイトルを取得
	title := doc.Find("title").Text()
	fmt.Println(title)
}

//Output:
//Example Domain
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/PuerkitoBio/goquery"
)

func ExampleScrape() {
	// 読み込むページ
	res, err := http.Get("https://zenn.dev/books/explore")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	// Bodyを読み込む
	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	// articleタグの中から著者とタイトルのclassを取得する
	doc.Find("article").Each(func(i int, s *goquery.Selection) {

		author := s.Find(".BookLargeLink_userName__jNbk5").Text()
		book := s.Find(".BookLargeLink_title__RqL6r").Text()
		if "" == author {
			author = s.Find(".BookLink_userName__avtjq").Text()
		}
		if "" == book {
			book = s.Find(".BookLink_title__b8hGg").Text()
		}
		fmt.Printf("%d 著者: %v / タイトル: %v\n", i, author, book)
	})
}

func main() {
	ExampleScrape()
}

//Output:
//0 著者: ふーが / タイトル: RailsチュートリアルのテストをRSpecで書き換える
//1 著者: yamatatsu / タイトル: AWS CDK ドキュメント(和訳メモ)
//⋮

参考にしたサイト: https://zetcode.com/golang/goquery/

colly

collyは、goquery をベースにしたライブラリのようです。
また、公式からDocumentationが公開されています。

package main

import (
	"fmt"

	"github.com/gocolly/colly"
)

func main() {

	c := colly.NewCollector()

	//ここでタイトルを取得
	c.OnHTML("title", func(e *colly.HTMLElement) {
		fmt.Println(e.Text)
	})

	c.Visit("https://example.com/")
}
package main

import (
	"fmt"

	"github.com/gocolly/colly"
)

func main() {

	c := colly.NewCollector()
	i := 0

	c.OnHTML("article", func(e *colly.HTMLElement) {

		i++
		book := e.DOM.Find("a > h3").Text()
		author := e.DOM.Find("div > a").Text()
		if author == "" {
			author = e.ChildText(".BookLink_userName__avtjq")
		}

		fmt.Printf("%d 著者: %s / タイトル: %s\n", i, author, book)
	})

	c.Visit("https://zenn.dev/books/explore")
}

参考にしたサイト: https://blog.webmatrices.com/web-scraping-with-golang-go-and-colly

感想

goqueryもcollyも日本語で書かれている記事が少なかったので、記事にしてみました。
collyの方はgoqueryを拡張した感じなので、出来ることが多そうな印象でした。
今後は、他言語でもスクレイピングを試してみたいので、Pythonあたりを調べてみようかなと考えております。