1 of 38

Goでブラウザ

業務を自動化する

@shuntaka

takahashi shunichi

2 of 38

@shuntaka_jp

普段:インフラ・サーバーサイド(AWS,C#,Python)

3 of 38

早速ですが.....

ブラウザで行う定型業務ありませんか?

4 of 38

私の日常的なブラウザ定型業務例

・着席した席をスプレッドシートに入力(弊社フリーアドレス)

・勤怠管理

・本番サーバーのアクセス申請

・領収書のとりまとめ

5 of 38

日常的なブラウザ業務がもたらす弊害

プログラマ美徳三原則(怠惰・傲慢・短気)に反する

地味な面倒さ故、後回しにして忘れる

知的創造業務に充てるべき時間が減る

6 of 38

そうだ、自動化しよう

7 of 38

Agouti (https://github.com/sclevine/agouti.git)

アグーチと発音する

8 of 38

ここから、agoutiの話です

知ってる方は、ごめんなさい🙇‍

9 of 38

agoutiの話

〜基礎的な使い方〜

10 of 38

使い方(ドライバーの作成、起動)

// driverを定義

var driver *agouti.WebDriver

// ブラウザにchromeを指定して起動(自動操作したいブラウザを指定)�driver = agouti.ChromeDriver(agouti.Browser("chrome"))�if err := driver.Start(); err != nil {� log.Fatalf("Faild to start driver %v\n", err)�}

// 関数終了時、ブラウザ終了defer driver.Stop()

11 of 38

続き(ページ遷移)

// 先程のdriverから、ページを取得

page, err := driver.NewPage()�if err != nil {� log.Fatalf("Faild to return page %v\n", err)�}

// Googleに遷移if err := page.Navigate("https://www.google.com/"); err != nil {� log.Fatalf("Faild to open page %v\n", err)�}

12 of 38

headlessモード(ブラウザ非表示モード)

// non-headless

driver = agouti.ChromeDriver(agouti.Browser("chrome"))

// headless

driver = agouti.ChromeDriver(� agouti.Browser("chrome"),� agouti.ChromeOptions(� "args",� []string{"--headless", "--disable-gpu"}),�)

13 of 38

agoutiの話

〜実際に自動化してみる〜

14 of 38

私の日常的なブラウザ定型業務

・着席した席をスプレッドシートに入力(弊社フリーアドレス)

・勤怠管理

・本番サーバーのアクセス申請

・領収書のとりまとめ

15 of 38

着席した席をスプレッドシートに入力

以下のようなフロー

Gsuite

ログイン処理

入力フォーム

遷移(席入力)

Gsuiteページ遷移

入力フォーム

入力処理

反映したスプレッドシート画面に遷移

16 of 38

着席した席をスプレッドシートに入力

以下のようなフロー

入力フォーム

遷移(席入力)

Gsuiteページ遷移

反映したスプレッドシート画面に遷移

Gsuite

ログイン処理

入力フォーム

入力処理

17 of 38

フォーム処理・ログイン処理方法

// ログイン画面遷移if err := page.Navigate("https://login..."); err != nil {� log.Fatalf("driver停止エラー:%v", err)�}

// ユーザー名入力・ボタン押下�page.FindByClass("input-text").Fill(conf.Gsuite.User)�page.FindByClass("input-button").Click()�

// パスワード入力・ボタン押下

page.Find("#password").Fill(conf.Gsuite.Password)�page.FindByClass("input-button").Click()

指定したタグに、値を入力

指定したタグをクリック

タグを指定

18 of 38

タグの指定方法は複数存在

19 of 38

困ったらDevTool(chrome)からCopy selector

タグの指定が思うように

行かない場合等...

20 of 38

私の日常的なブラウザ定型業務

・着席した席をスプレッドシートに入力(弊社フリーアドレス)

・勤怠管理

・本番サーバーのアクセス申請

・領収書のとりまとめ

 → 同じ要領で解決

21 of 38

私の日常的なブラウザ定型業務

・着席した席をスプレッドシートに入力(弊社フリーアドレス)

・勤怠管理

・本番サーバーのアクセス申請

・領収書のとりまとめ

22 of 38

本番サーバーのアクセス申請

毎朝月曜日、5日分申請を行う

・申請日時

・申請者

・作業内容(テンプレで良い)

goroutineを用いて、5日分並列処理で申請を作成

をフォームに記載・申請

入力フォーム

遷移

入力処理・申請

(1日分)

23 of 38

パラレル実行(フォーム入力・申請)

wg := &sync.WaitGroup{}�filDay := time.Now()

// 1週間分の申請をパラレルに実行for i := 0; i < 5; i++ {� go func(day string) {� wg.Add(1)

Fillday(day)� wg.Done()� }(filDay.Format("20060102"))

filDay = filDay.AddDate(0, 0, 1) // 日付を加算�}�wg.Wait()

※イメージ

日付(引数指定)のサーバー利用申請してくれる関数

24 of 38

私の日常的なブラウザ定型業務

・着席した席をスプレッドシートに入力(弊社フリーアドレス)

・勤怠管理

・本番サーバーのアクセス申請

・領収書のとりまとめ

25 of 38

領収書のとりまとめ

・半年に1回あるイベント

・ブラウザで、月毎の領収書画面をそれぞれ印刷(計6枚)

↪ホッチキス止め

↪担当者に提出

26 of 38

こう自動化する・・

結合

印刷

🙍‍

提出

自動化領域

ペーパーレス化

1月分

2月分

3月分

...

27 of 38

行う処理

ページ遷移(agouti)

タグの指定、操作(agouti)

・スクリーンショット機能(agouti)

・画像合成(image)

28 of 38

スクリーンショット機能

// 領収書のポップアップ表示されるボタンタグを押下�page.Find("#contentInner > div.table-history > p > a").Click()

// ポップアップにウィンドウを遷移�page.NextWindow()

// スクリーンショットを取得�page.Screenshot(imgfolder + "/" + baseobj.Start + ".png")

// 元のウィンドウに戻る

page.NextWindow()

保存するパス・ファイル名を指定

29 of 38

取得した画像数を元に、*RGBAを生成

合成後の画像サイズ

// 取得した画像枚数を元に

// 合成する画像イメージのa,b点を算出

a := image.Point{a.X, a.Y}

b := image.Point{b.X, b.Y}

// *RGBAインスタンスを生成

rgba := image.NewRGBA(a, b)

rgba

x

y

a

b

30 of 38

rgbaに、スクショした画像をDrawしていく

合成後の画像サイズ

draw.Draw(

rgba,

Rectangle{p1,p2},

1月の領収書画像(Image型),

image.Point{0, 0},

draw.Src

)

rgba

1月の領収書画像

p2

rgba

x

y

p1

31 of 38

同じ要領で画像をDrawしていく

合成後の画像サイズ

1月の領収書画像

draw.Draw(

rgba,

Rectangle{p1,p2},

2月の領収書画像(Image型),

image.Point{0, 0},

draw.Src

)

rgba

2月の領収書画像

p2

x

y

p1

32 of 38

最後に画像エンコード処理を実行

合成後の画像サイズ

5月の領収書画像

out, _ := os.Create("out.png")�// エンコード

png.Encode(out, rgba)

/*

(0,0)-(670,654) 201701.png

(670,0)-(1340,654) 201702.png

(0,654)-(670,1308) 201803.png

*/

rgba

6月の領収書画像

3月の領収書画像

4月の領収書画像

1月の領収書画像

2月の領収書画像

x

y

33 of 38

Goでブラウザ業務を自動化してみて

34 of 38

agoutiに関して

・ブラウザの基本操作が、agoutiで直感的に書ける

・ヘッドレスモードは、

 操作内容が分からないので、配布時はデフォルトオフ

・クローリング・スクレイピング用途でも使えそう

35 of 38

Goの得意分野が生きる

・ワンバイナリ = 配布が簡単

・クロスコンパイル = より多くの人(win,Mac)に配布可能

36 of 38

改善点

・chromedriverを静的リンクしたい(一緒に配布したくない)

37 of 38

寄せられた苦情(おまけ)

・config.jsonってファイルが開けないんだけど(営業)

・win32で動かないんだけど(総務)

→配布先のITリテラシーを考慮したい人生だった。

38 of 38

ご清聴頂きありがとうございました!

Gopher道場 #3開催して頂きありがとうございました!

@tenntennさん、メンターさんありがとうございました!