じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

go-rod/rod でブラウザ自動化とWebスクレイピングをやっていく

はじめに

Webスクレイピングの需要が高まる中、Goで実装する機会が増えてきました(よね?)。Goの豊富な標準ライブラリとシンプルな文法は、スクレイピングのような自動化タスクと非常に相性が良いです。 が、今回はGoの有力なスクレイピングライブラリの1つである go-rod/rod の特徴を掘り下げながら、実際に現在所属している組織のサイト3-shake.comのWebサイトをスクレイピングする方法をご紹介します(広告的に許してくれ)。

github.com

go-rod/rod の概要と特徴

go-rod/rod`は、ChromeDevToolsプロトコルを利用したブラウザ自動化とスクレイピングのためのハイレベルなドライバーライブラリです。単なるHTMLの取得だけでなく、ブラウザ上の操作を自動化できるのが大きな特長です。

主な特徴は以下の通りです:

つまり、ブラウザ上で手動で行える操作のほとんどを自動化できるわけです。 これによりJavaScriptで動的に生成されるモダンなWebサイトに対しても、自在にスクレイピングを行えます。

go-rod.github.io

また、Goらしいシンプルで読みやすいAPIを提供しているのも魅力です。実装の詳細を隠蔽しつつ、柔軟で強力な機能を直感的に使用できるよう設計されています。

インストールと基本的な使い方

go-rod/rod は次のコマンドで簡単にインストールできます。

go get github.com/go-rod/rod

インストール後、Goのコードから次のように呼び出すことでブラウザを起動できます。

package main

import "github.com/go-rod/rod"

func main() {
    browser := rod.New().MustConnect().MustPage("https://3-shake.com/")
    browser.MustWaitStable().MustScreenshot("3-shake.png")
    defer browser.MustClose()
}

ここではブラウザオブジェクトを生成し、MustConnect() でブラウザプロセスに接続して続いて MustPage() を使ってページを開きます。それをMustScreenshot()を使ってスクリーンショットを撮っていきます。

こちらがスクリーンショットです。

defer 文で最後にブラウザを閉じるのを忘れずに。

これだけでブラウザの自動操作の準備は整いました。めちゃくちゃにシンプルですね。

3-shake.com のスクレイピング

それでは、実際に https://3-shake.com のWebサイトをスクレイピングしてみましょう。 今回は以下の情報を抽出することを目標とします。

  • 全サービスの名前と説明文
  • 全ニュースのタイトルと日付

サービス情報の抽出

まず、サービス一覧を表示している要素を特定します。 サイトを開発者ツールで覗いてみると、各サービスが以下のようなDOM構造になっていることがわかります。

<li class="services__item">
    <div class="services__block js-inview" data-inview-x="30" data-inview-s="700">
        <p class="services__pic js-parallax">
            <img src="path/to/image">
        </p>
    </div>
    <div class="services__block js-inview" data-inview-x="-30" data-inview-s="700">
        <div class="services__texts">
            <div class="services__name">
                <a href="path/to/service" target="_blank">
                    <img src="path/to/logo">
                    <p>サービス名<br><span>サービス名ふりがな</span></p>
                </a>
            </div>
            <p class="services__lead">サービスの説明文</p>
            <p class="services__link p-text--link">
                <a href="path/to/service" target="_blank">サービスサイトへ<i class="p-icon-arrow-right"></i></a>
            </p>
        </div>
    </div>
</li>

これを元に、スクレイピングコードを書いていきます。

// サービス情報をスクレイピング
services := page.MustElements("li.services__item")
for _, service := range services {
    name := service.MustElement(".services__name").MustText()
    description := service.MustElement(".services__lead").MustText()
    fmt.Printf("サービス名: %s\n", name)
    fmt.Printf("説明文: %s\n", description)
    fmt.Println("---")
}

MustElements()li.services__item にマッチする要素を全て取得し、それぞれの要素から MustElement()MustText() でサービス名と説明文を抜き出しています。

ニュース情報の抽出

次にニュース一覧を取得しましょう。 こちらは以下のようなDOM構造になっています。

<div class="p-articles js-news__target l-col l-col--list js-inview-box" data-inview-y="15">                   
    <div class="p-articles__item l-col__block--4 is-show">
        <div class="p-articles__thumb">
            <a class="p-articles__link" href="path/to/news" target="_self">
                <span style="background-image: url('path/to/image');"></span>
            </a>
        </div>
        <div class="p-articles__info">
            <p class="p-articles__date">YYYY.MM.DD</p>
            <ul class="p-categories">
                <li class="p-categories__item">
                    <a href="path/to/category">カテゴリ名</a>
                </li>
            </ul>
        </div>
        <p class="p-articles__text">
            <a class="p-articles__link" href="path/to/news" target="_self">ニュースタイトル</a>
        </p>
    </div>
</div>

これを元にスクレイピングコードを書きます。

package main

import (
    "fmt"

    "github.com/go-rod/rod"
)

func main() {
    browser := rod.New().MustConnect()
    defer browser.MustClose()

    page := browser.MustPage("https://3-shake.com/")

    // ニュース情報をスクレイピング
    newsList := page.MustElement(".p-articles.js-news__target.l-col.l-col--list.js-inview-box")
    newsItems := newsList.MustElements(".p-articles__item")

    for _, item := range newsItems {
        title := item.MustElement(".p-articles__text").MustText()
        date := item.MustElement(".p-articles__date").MustText()
        fmt.Printf("タイトル: %s\n", title)
        fmt.Printf("日付: %s\n", date)
        fmt.Println("---")
    }
}

ニュースが .p-articles.js-news__target.l-col.l-col--list.js-inview-box の中にあるので、まずはその要素を MustElement() で取得します。 そこから .p-articles__item を全て取り出し、タイトルと日付を抽出しています。

これで目的の情報が取得できました。 実際に出力してみると次のようになります。

実際にテキストも転記しておく、、、。

タイトル: 自動脆弱性診断ツール「Securify」、AI技術を駆使する「ai6」が導入
日付: 2024.04.05
---
タイトル: 『ferret』に寄稿記事が掲載されました。
日付: 2024.03.25
---
タイトル: 自動脆弱性診断ツール「Securify」、大手通信販売会社「フェリシモ」が導入
日付: 2024.03.22
---
タイトル: Relance フリーランス協会の「認定マッチング事業者」として今年も正式採択
日付: 2024.04.03
---

各サービスの名前と説明文、ニュースのタイトルと日付がきちんと取得できていますね。フォームの自動入力や並列数を上げての負荷試験を兼ねたE2Eテストも行いましたが流石に迷惑になるので自分のユースケースにあったコードをexamples から探して下さい github.com

ヘッドレスモードでのスクレイピング

go-rod/rodの大きな特徴の1つに、ヘッドレスモードでのブラウザ操作があります。 ヘッドレスモードとは、GUIを持たない状態でブラウザを起動し、バックグラウンドで動作させる機能です。

通常、ブラウザを自動操作する際にはブラウザウィンドウが立ち上がりますが、ヘッドレスモードならそれがありません。 その分リソースを節約でき、サーバー上での実行に向いています。CIパイプライン内でのテストなどにも利用できます。負荷試験などにも使えるかもしれないので調査中です。

    // github.com/go-rod/rod/lib/launcher を利用する
    // ヘッドレスブラウザを起動する
    url := launcher.New().MustLaunch()
    browser := rod.New().ControlURL(url).MustConnect("https://3-shake.com/")
    // スクレイピング対象のページを指定する
    page := browser.MustPage()

launcher はやっていくと必要になる場面が出てくるので一通り目を通して置くと後に応用が効くかもです。

github.com

おわりに

今回はGoのスクレイピングライブラリ go-rod/rod の特徴を確認しながら、Webサイト https://3-shake.com/ から情報を抽出する方法を紹介しました。

go-rod/rod の優れた点は、ChromeのDevToolsプロトコルを利用してブラウザを直接操作できることです。 これによりサーバーサイドだけでなく、クライアントサイドで動的に生成されるコンテンツに対しても柔軟にスクレイピングを行えます。

Goのシンプルな文法とあいまって、簡潔かつパワフルなスクレイピングスクリプトが書けるのが魅力ですね。ぜひ皆さんも go-rod/rod を使って色々なWebサイトに挑戦してみてください。

スクレイピングが必要とされるシーンは今後ますます増えていくでしょう。Goと go-rod/rod を使いこなせば、そんな要望にも難なく応えられるはずです。快適で効率的なスクレイピングライフを送っていきたいと思います

参考資料