LoginSignup
11
8

More than 5 years have passed since last update.

Chrome ヘッドレスでファイルダウンロード[Golang + chromedp]

Last updated at Posted at 2018-04-16

やったこと

  • Chrome Devtools Protocolを用いて、HeadlessChromeでファイルダウンロードをする。

実際のコード

  • 例としてMoneyForwardにログインして、家計簿のcsvをダウンロードするコードを書いてみる。
package main

import (
    "context"
    "log"
    "time"
    "os"

    "github.com/chromedp/chromedp"
    "github.com/chromedp/chromedp/runner"
    "github.com/chromedp/cdproto/cdp"
    "github.com/chromedp/cdproto/page"
)

func main() {
    var err error

    // create context
    ctxt, cancel := context.WithCancel(context.Background())
    defer cancel()

    // create chrome instance with headless mode
    chrome := "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
    c, err := chromedp.New(ctxt, chromedp.WithRunnerOptions(runner.HeadlessPathPort(chrome, 9992)))
    if err != nil {
        log.Fatal(err)
    }

    host := "https://moneyforward.com"
    user := os.Getenv("MF_USER")
    pass := os.Getenv("MF_PASS")

    // run task
    err = c.Run(ctxt, doScrape(host, user, pass))
    if err != nil {
        log.Fatal(err)
    }

    log.Println("Scrape Done.")

    // shutdown chrome
    err = c.Shutdown(ctxt)
    if err != nil {
        log.Println("down-err")
        log.Fatal(err)
    }
    log.Println("Chrome Shutdown.")
}

func doScrape(host, user, pass string) chromedp.Tasks {
    return chromedp.Tasks{
        // configure to download behavior
        chromedp.ActionFunc(func(ctxt context.Context, h cdp.Executor) error {
            err := page.SetDownloadBehavior("allow").WithDownloadPath("/tmp").Do(ctxt, h)
            if err != nil {
                return err
            }
            return nil
        }),
        chromedp.Navigate(host + "/users/sign_in"),
        chromedp.SendKeys("//input[@id='sign_in_session_service_email']", user),
        chromedp.SendKeys("//input[@id='sign_in_session_service_password']", pass),
        chromedp.Click("//input[@id='login-btn-sumit']"),
        // login
        chromedp.WaitVisible(".logo-image", chromedp.ByQuery),
        // move to cashflow page
        chromedp.Navigate(host + "/cf"),
        // download file
        chromedp.Click("//*[@id='js-dl-area']"),
        chromedp.Click("//*[@id='js-csv-dl']"),
    }
}

モチベーション

  • URIを持たないファイルリソースや認証の先にあるダウンロードリンクからファイルを取得したかった。
  • Golangにした理由はコマンドラインツールにしたかったため。
    • 取得したファイルを標準出力にはけば、あとはパイプでつないで編集→別ツールにインポート、みたいなワークフローを想定。

所感

  • Chrome Devtools Protocolおもしろい!ブラウザで出来ることのAPIが豊富に用意されているので、Golangとの組み合わせはブラウザ操作をガシガシ自動化していくよいツールに思えた。
  • chromedpとcdprotoの設計がかなり抽象レイヤを挟んでいて、読んでいて面白い反面、少々冗長に思えた。私が読み切れていないだけかもしれない。

参照リンク

  • Headless Chromeでファイルをダウンロードする
    • 「chrome headless ファイルダウンロード」でtopでヒットする情報。こちらでdownloadBehaviorの存在を知ることができました。ありがとうございます。
  • chromedp
    • golangでchrome devtool protocolを扱うためのフロント
  • cdproto
    • chromedpから参照されるライブラリ群
11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8