🤖

【Go】Web スクレイピング(colly)

2023/10/27に公開

はじめに

Go で Web スクレイピングする方法をまとめてみます。
※ スクレイピング自体は自己責任でお願いします。

スクレイピングの前に確認すること

スクレイピングの前に、目的のサイトがスクレイピングを許可しているのかを確認する必要があります。
利用規約と robots.txt を見ておきましょう。

利用規約を確認する

まずはスクレイピング先のサイトにある利用規約を確認する必要があります。
サイトによっては、利用規約にプログラムからのアクセスを禁止している項目があるからです。
大体は「禁止事項」の欄に書いてあります。

例として Zenn の利用規約を確認してみました。

※ 2023年9月時点の利用規約の第4条(禁止事項)です。

  1. 法令または公序良俗に違反する行為
  2. 犯罪行為に関連する行為
  3. 運営者のサーバーまたはネットワークの機能を破壊したり、妨害したりする行為
  4. 本サービスの運営を妨害する行為、または妨害するおそれのある行為
  5. 他者の個人情報等を収集または蓄積する行為
  6. 他者に成りすます行為
  7. 反社会的勢力に対して直接的または間接的に利益を供与する行為
  8. 本サービスの利用者および運営者、第三者の知的財産権、肖像権、プライバシー、名誉その他の権利または利益を侵害する行為
  9. 何らかの手段により、本サービス上の有料コンテンツに支払いなくアクセスする行為
  10. 他の利用者および第三者を欺く虚偽の内容を記載する行為
  11. スパムとみなされる行為(機械により自動生成された文章の投稿や同一内容の文章を繰り返し投稿する行為など)
  12. 過度に暴力的な表現、露骨な性的表現、人種、国籍、信条、性別、社会的身分、門地等による差別につながる表現、自殺、自傷行為、薬物乱用を誘引または助長する表現、他人に不快感を与える表現等、不適切な内容を投稿する行為
  13. 性行為やわいせつな行為を目的とする行為、面識のない異性との出会いや交際を目的とする行為、他者に対する嫌がらせや誹謗中傷
  14. 宗教活動または宗教団体への勧誘行為
  15. その他、運営者が不適切と判断する行為

上記の利用規約を読み解くことで、サーバーやネットワークの邪魔にならない範囲でならスクレイピングしても特に問題がないと分かりました。

robots.txt を確認する

利用規約の次は robots.txt を確認します。
robots.txt とは、クローラーにサイト内でアクセスしても良いページを伝えるためのテキストファイルのことです。

実際に Zenn の robots.txt を確認し、Zenn でどのページがスクレイピングを許可されているのかを読み解いていきます。

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/sitemaps/_index.xml

上記の robots.txt では、指定された User-agent に対して、どのページでクロールが不可なのかを Disallow で指定しています。
読み解くと、全てのページで以下のユーザーエージェントからのクロールは許可していません。
ただし、その他ユーザーエージェントからのクロールは許可しています。

  • Yahoo Pipes 1.0
  • 008
  • voltron
  • Bytespider
  • Livelapbot
  • Megalodon
  • ia_archiver

Zenn のどのページをスクレイピングしても問題ないことが分かりました。

仮に以下の記載があった場合、サイト内のクロールが許可されていないためスクレイピングはあきらめましょう。

User-agent: *
Disallow: /

robots.txt の詳細な読み方が知りたい方は、Google による robots.txt の指定の解釈を参照してください。

実際にスクレイピングする

本記事では colly(v2.1.0)を使ってスクレイピングを行います。
スクレイピングに使うパッケージですが、基本的にスター数が一番多い colly を選んでおけば問題ありません。

また、スクレイピング先は Zenn とします。

colly をインストール

まずは colly をインストールしましょう。

go get github.com/gocolly/colly/v2@v2.1.0

colly の基本的な使い方

colly の基本的な使い方は以下です。

  1. colly.NewCollectorCollector を生成する
  2. Collector にコールバック関数を登録する(※ 以下は一部)
    • 指定した要素が見つかった時に処理を実行したい
    • リクエストでエラーが発生した時に処理を実行したい
    • 全てのリクエストで処理を実行したい
    • 全てのレスポンスで処理を実行したい
  3. Visit メソッドでスクレイピングを開始する

使い方に少し癖がありますが、オプションなどが豊富で非常に便利です。

Zenn から Go の最新記事を取得してみる

Go の最新記事を一覧で取得してみます。

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/gocolly/colly/v2"
)

const (
	targetDomain = "zenn.dev"
	targetURL    = "https://" + targetDomain
)

func main() {
	c := colly.NewCollector(
		// Zenn 以外のアクセスを許可しない
		colly.AllowedDomains(targetDomain),
		// ./cache でレスポンスをキャッシュする
		colly.CacheDir("./cache"),
		// アクセスするページの再帰の深さを設定
		colly.MaxDepth(2),
		// ユーザーエージェントを設定
		colly.UserAgent("Sample-Scraper"),
	)

	// リクエスト間で1~2秒の時間を空ける
	c.Limit(&colly.LimitRule{
		DomainGlob:  targetDomain,
		Delay:       time.Second,
		RandomDelay: time.Second,
	})

	// 全ての article 要素に対して実行される関数
	c.OnHTML("article", func(e *colly.HTMLElement) {
		title := e.DOM.Find("h2").Text()
		href, ok := e.DOM.Find("a").Attr("href")
		if !ok {
			href = "Not Link!"
		}
		log.Printf("Title: %s, URL: %s%s", title, targetURL, href)
	})

	c.OnHTML("a.Button_secondary__cM38g.Button_baseStyle__Vhn6Y.Button_medium__STW9Z.Button_shadow__3xqQY", func(e *colly.HTMLElement) {
		// 次のページに遷移する
		e.Request.Visit(e.Attr("href"))
	})

	// エラー発生時に実行される関数
	c.OnError(func(r *colly.Response, err error) {
		log.Fatalln("Request URL:", r.Request.URL, "failed with response:", r, "\nError:", err)
	})

	url := fmt.Sprintf("%s/topics/go?order=latest", targetURL)
	c.Visit(url)
}

実行結果は以下です。

2023/10/26 23:08:30 Title: 続:Goa の example が試しやすくなりました, URL: https://zenn.dev/ikawaha/articles/20231026-26b8fba56c35e9
2023/10/26 23:08:30 Title: paclear: PAC-MANによるターミナルクリーニング, URL: https://zenn.dev/orangekame/articles/b825bdb799dce3
2023/10/26 23:08:30 Title: 【コードレビュー】可読性向上:ロジックの単純化テクニック, URL: https://zenn.dev/kushidam/articles/006_92cc97d811ddf0
2023/10/26 23:08:30 Title: goで http.Requestのbodyが途中でエラーを吐いてもサーバーが見落とすパターン, URL: https://zenn.dev/sess/articles/d63e2b5f524075
2023/10/26 23:08:30 Title: 【Go言語】コマンドライン引数に指定したファイルの中身を取得する方法, URL: https://zenn.dev/midra_lab/articles/23d312f4f72e4f
2023/10/26 23:08:30 Title: OpenTelemetryの計装をデバッグする, URL: https://zenn.dev/vaxila_labs/articles/3d1b0c80cbc9c9
2023/10/26 23:08:30 Title: 【Go】gRPC-connectでEmpty型を使ってみた, URL: https://zenn.dev/someone7140/articles/6868e14fbadb95
...

無事取得できました。

さいごに

Go で Web スクレイピングする方法をまとめてみました。
スクレイピングの実装自体は比較的簡単ですが、相手のサイトに配慮しながら行う必要があります。
一歩間違えれば DoS 攻撃になってしまうので気をつけましょう!

Discussion