電波ビーチ

☆(ゝω・)v

chromedp+goqueryを使ったスクレイピングのスニペット

タイトルですべてを語っているだけの自分用メモ

背景

そういう機会に直面してしまった。pythonなら余裕だがpython/Node以外だとそういえばやったことがない。せっかくなのでGoでやる。以上

chromedpは初めて使った

chromedp/chromedp - github

四方山

また、最初テキトーにググった内容をノールックでパクろう参考にしようとしてたため、日本語記事ではchromedpより多くヒットしたagoutiを使おうとしてたが、コピペサンプルを実装して「あれ?なんか動かんな」ってなってから初めて調べて、1年前にプロジェクト終了のお知らせを出してたのを知った。使用するライブラリのreadmeくらいちゃんと読もうね....

chromedpはagoutiと違ってseleniumやchromedriverといったwebdriverのたぐいを用意しなくても動くのが便利。昨今のスクレイピングツールはだいたいそういう感じなのかもしれないが知らない

コード

素晴らしいことにchromedpのリポジトリにはたくさんの(完動する)サンプルがある。ろくにリファレンス読まずとも今回やりたかった用途程度ならば剽窃書けてしまった。

「JSがロードされたあとの、ブラウザでレンダリングされた結果のhtml」が欲しかったのである。この用途でいうと、以下のサンプルでいい

なお、今回のはホントにイージーなので、むしろgoqueryを使わなくてもいいのだが、怠惰なので使った。ついでにUser-Agentをランダムに生成してくれるパッケージを使ってみた。スクレイピング仕草(?)

github.com

package main

import (
    "context"
    "fmt"
    "log"
    "strings"
    "time"

    "github.com/DataHenHQ/useragent"
    "github.com/PuerkitoBio/goquery"
    "github.com/chromedp/chromedp"
)

func main() {
    // user-agent
    ua, err := useragent.Desktop()
    if err != nil {
        log.Fatalln(err)
    }
    // options いろいろあるけど今回はuser-agentだけ
    opts := append(chromedp.DefaultExecAllocatorOptions[:],
        chromedp.UserAgent(ua),
    )
    ctx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
    defer cancel()

    // create context
    ctx, cancel = chromedp.NewContext(ctx)
    defer cancel()
    // time out
    ctx, cancel = context.WithTimeout(ctx, 50*time.Second)
    defer cancel()

    var wholeHtml string
    // run task list
    err = chromedp.Run(
        ctx,
        chromedp.Navigate("ターゲットのサイトURL"),
        chromedp.Sleep(1000*time.Millisecond), // いちおうスクレイピング仕草(?)
        chromedp.WaitVisible("div.mb-2:nth-child(2)"),
        chromedp.OuterHTML("html", &wholeHtml, chromedp.ByQuery),
    )
    if err != nil {
        log.Fatalln(err)
    }
    dom, err := goquery.NewDocumentFromReader(strings.NewReader(wholeHtml))
    if err != nil {
        log.Fatalln(err)
    }
    a := dom.Find("div.text-base:nth-child(1)").Text()
    b := dom.Find("div.mb-2:nth-child(2)").Text()
    fmt.Printf("GET TEXT:\nA: %s\nB: %s\n", a, b)
}

CSSセレクターは横着してfirefoxのdevtoolの「コピー->CSSセレクター」でコピーしたものをそのまんま貼り付けただけである。うーん怠惰

参考

Golang+chromedp+goquery 简单爬取动态数据

全体的な構成を参考にした(bilibiliのサイトなのでちょっと重い)

chromedp で Chrome を見える状態(not headless)で起動して、かつ終了しないようにする - Qiita

オプションの書き方を参考にした