Go言語で簡単なWebスクレイピング

Blog Single

最近Go言語をかじりはじめました。

今回は、Go言語を使って自分が今まで投稿した記事のそれぞれの文字数を取得するシンプルなWebスクレイピングのプログラムを作成します。

ライブラリのインストール

以下のライブラリを用いて、スクレイピングを行います。
GitHub – PuerkitoBio/goquery: A little like that j-thing, only in Go.

jQueryライクにhtmlを扱うことができます。

ライブラリのインストール

go get github.com/PuerkitoBio/goquery

プログラムの作成

今まで投稿してきた記事のそれぞれの文字数を取得したいので、まずは今まで投稿した記事のURLを取得するプログラムを作成します。

以下のリンクのページに今まで投稿した記事の一覧があるので、ここからURLを取得します。
MatsumotoKazuki | FOX HOUND Tech

このページから、jQueryで、投稿した記事のリンクを取得する処理を書くと以下のようになります。

var urls = [];
$('.title a').each(function(){
    var url = $(this).attr('href');
    urls.push(url);
});

これをGo言語とgoqueryを使って書いた場合以下のようになります。

var urls []string
doc.Find(".title a").Each(func(_ int, s *goquery.Selection) {
    url, _ := s.Attr("href")
    urls = append(urls, url)
})

これを使って、実際にページのリンクを標準出力するプログラムを作成します。

scrapeSample.go

package main

import (
    "fmt"
    "github.com/PuerkitoBio/goquery"
)

func main() {
    // 投稿した記事の一覧ページのリンクから、ドキュメントを取得
    doc, err := goquery.NewDocument("https://blog.fox-hound.tech/author/kazuki-matsumoto/")
    if err != nil {
        fmt.Println(err)
    }
    // 投稿した記事のリンクのurlを配列に格納
    var urls []string
    doc.Find(".title a").Each(func(_ int, s *goquery.Selection) {
        url, _ := s.Attr("href")
        urls = append(urls, url)
    })
    // 出力
    fmt.Println(urls)
}

実行

go run scrapeSample.go

実行結果

[https://blog.fox-hound.tech/388/ https://blog.fox-hound.tech/329/ https://blog.fox-hound.tech/299/ https://blog.fox-hound.tech/227/ https://blog.fox-hound.tech/215/ https://blog.fox-hound.tech/163/]

これで正しく投稿した記事のURLが取得できているので、あとはそれぞれの記事の文章の文字数をカウントする処理を追加します。

単純に記事の文字列を取得してlen()を使って文字数を取得しようとすると、日本語はマルチバイト文字なので文字数が正しい値になりません。
そのため、unicode/utf8パッケージのRuneCountInString()を用いて文字数をカウントします。
unicode/utf8#RuneCountInString

あとは、先ほどと同じようにセレクタを指定して対象の文字列の取得を行います。

jQuery

$(".post--item > div.post--content").text();

goquery

doc.Find(".post--item > div.post--content").Text()

これらを用いて、プログラムを作成します。
scrapeSample.go

package main

import (
    "fmt"
    "github.com/PuerkitoBio/goquery"
    "unicode/utf8"
)

func main() {
    // 投稿した記事の一覧ページのリンクから、記事のドキュメントを取得
    doc, err := goquery.NewDocument("https://blog.fox-hound.tech/author/kazuki-matsumoto/")
    if err != nil {
        fmt.Println(err)
    }
    // 投稿した記事のリンクのurlを配列に格納
    var urls []string
    doc.Find(".title a").Each(func(_ int, s *goquery.Selection) {
        url, _ := s.Attr("href")
        urls = append(urls, url)
    })

    for i := 0; i < len(urls); i++ {
        fmt.Println(urls[i])
        doc, err := goquery.NewDocument(urls[i])
        if err != nil {
            fmt.Print("url scarapping failed")
        }
        title := doc.Find(".post--item .title h2").Text()
        fmt.Println(title)
        text := doc.Find(".post--item > div.post--content").Text()

        // 文字数をカウント(日本語を含むため、utf8.RuneCountInStringを利用)
        fmt.Printf("文字数: %d 字\n\n", utf8.RuneCountInString(text))
    }
}

実行

go run scrapeSample.go

実行結果

うまく実行されました。

まだまだGo言語に触りはじめたばかりですが、パッケージの使いやすさ、標準ライブラリのドキュメントが整理されていて見やすい、言語仕様がシンプル、実行速度が速いなど様々なメリットがあり、書いていて楽しいので、興味を持った方はぜひ触ってみてください。

webスクレイピングは、人間がアクセスするwebサイトにスピードとはアクセスするのとは比べ物にならないくらい大量の処理も行えますが、サーバーに過度な負荷を与える可能性もあるので、サーバーへの負担を考えてプログラムを作成しましょう。

Posted by MatsumotoKazuki
PHPやJavaで開発を行っているエンジニア。 LOLというゲームの試合を観戦するのが好きです。

Other Posts: