Slide 1

Slide 1 text

Goでブラウザ 業務を自動化する @shuntaka takahashi shunichi

Slide 2

Slide 2 text

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


Slide 3

Slide 3 text

早速ですが..... ブラウザで行う定型業務ありませんか?

Slide 4

Slide 4 text

私の日常的なブラウザ定型業務例
 ・着席した席をスプレッドシートに入力(弊社フリーアドレス)
 ・勤怠管理
 ・本番サーバーのアクセス申請
 ・領収書のとりまとめ


Slide 5

Slide 5 text

日常的なブラウザ業務がもたらす弊害
 ・プログラマ美徳三原則(怠惰・傲慢・短気)に反する
 ・地味な面倒さ故、後回しにして忘れる
 ・知的創造業務に充てるべき時間が減る


Slide 6

Slide 6 text

そうだ、自動化しよう

Slide 7

Slide 7 text

Agouti (https://github.com/sclevine/agouti.git) アグーチと発音する

Slide 8

Slide 8 text

ここから、agoutiの話です 知ってる方は、ごめんなさい🙇

Slide 9

Slide 9 text

agoutiの話 〜基礎的な使い方〜

Slide 10

Slide 10 text

使い方(ドライバーの作成、起動) // 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()

Slide 11

Slide 11 text

続き(ページ遷移) // 先程の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) }

Slide 12

Slide 12 text

headlessモード(ブラウザ非表示モード) // non-headless driver = agouti.ChromeDriver(agouti.Browser("chrome")) // headless driver = agouti.ChromeDriver( agouti.Browser("chrome"), agouti.ChromeOptions( "args", []string{"--headless", "--disable-gpu"}), )

Slide 13

Slide 13 text

agoutiの話 〜実際に自動化してみる〜

Slide 14

Slide 14 text

私の日常的なブラウザ定型業務
 ・着席した席をスプレッドシートに入力(弊社フリーアドレス)
 ・勤怠管理
 ・本番サーバーのアクセス申請
 ・領収書のとりまとめ


Slide 15

Slide 15 text

着席した席をスプレッドシートに入力
 以下のようなフロー
 Gsuite
 ログイン処理
 入力フォーム
 遷移(席入力)
 Gsuiteページ 遷移
 入力フォーム
 入力処理
 反映したスプ レッドシート画 面に遷移


Slide 16

Slide 16 text

着席した席をスプレッドシートに入力
 以下のようなフロー
 入力フォーム
 遷移(席入力)
 Gsuiteページ 遷移
 反映したスプ レッドシート画 面に遷移
 Gsuite
 ログイン処理
 入力フォーム
 入力処理


Slide 17

Slide 17 text

フォーム処理・ログイン処理方法 // ログイン画面遷移 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() 指定したタグに、値を入力 指定したタグをクリック タグを指定

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

困ったらDevTool(chrome)からCopy selector タグの指定が思うように 行かない場合等...

Slide 20

Slide 20 text

私の日常的なブラウザ定型業務
 ・着席した席をスプレッドシートに入力(弊社フリーアドレス)
 ・勤怠管理
 ・本番サーバーのアクセス申請
 ・領収書のとりまとめ
  → 同じ要領で解決


Slide 21

Slide 21 text

私の日常的なブラウザ定型業務
 ・着席した席をスプレッドシートに入力(弊社フリーアドレス)
 ・勤怠管理
 ・本番サーバーのアクセス申請
 ・領収書のとりまとめ


Slide 22

Slide 22 text

本番サーバーのアクセス申請
 毎朝月曜日、5日分申請を行う
 ・申請日時
 ・申請者
 ・作業内容(テンプレで良い)
 入力フォーム
 遷移
 入力処理・申請
 (1日分)
 goroutineを用いて、5日分並列処理で申請を作成
 をフォームに記載・申請


Slide 23

Slide 23 text

パラレル実行(フォーム入力・申請) 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() ※イメージ 日付(引数指定)のサーバー利用申請してくれる関数

Slide 24

Slide 24 text

私の日常的なブラウザ定型業務
 ・着席した席をスプレッドシートに入力(弊社フリーアドレス)
 ・勤怠管理
 ・本番サーバーのアクセス申請
 ・領収書のとりまとめ


Slide 25

Slide 25 text

領収書のとりまとめ
 ・半年に1回あるイベント
 ・ブラウザで、月毎の領収書画面をそれぞれ印刷(計6枚)
 ↪ホッチキス止め
 ↪担当者に提出


Slide 26

Slide 26 text

こう自動化する・・
 1月分 2月分 3月分 ... 結合
 印刷
 🙍 提出
 自動化領域
 ペーパーレス化


Slide 27

Slide 27 text

行う処理
 ・ページ遷移(agouti)
 ・タグの指定、操作(agouti)
 ・スクリーンショット機能(agouti)
 ・画像合成(image)


Slide 28

Slide 28 text

// 領収書のポップアップ表示されるボタンタグを押下 page.Find("#contentInner > div.table-history > p > a").Click() // ポップアップにウィンドウを遷移 page.NextWindow() // スクリーンショットを取得 page.Screenshot(imgfolder + "/" + baseobj.Start + ".png") // 元のウィンドウに戻る page.NextWindow() スクリーンショット機能 保存するパス・ファイル名を指定

Slide 29

Slide 29 text

取得した画像数を元に、*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

Slide 30

Slide 30 text

rgbaに、スクショした画像をDrawしていく 合成後の画像サイズ draw.Draw( rgba, Rectangle{p1,p2}, 1月の領収書画像(Image型), image.Point{0, 0}, draw.Src ) rgba 1月の領収書画像 p2 rgba x y p1

Slide 31

Slide 31 text

同じ要領で画像をDrawしていく 合成後の画像サイズ 1月の領収書画像 draw.Draw( rgba, Rectangle{p1,p2}, 2月の領収書画像(Image型), image.Point{0, 0}, draw.Src ) rgba 2月の領収書画像 p2 x y p1

Slide 32

Slide 32 text

最後に画像エンコード処理を実行 合成後の画像サイズ 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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

agoutiに関して ・ブラウザの基本操作が、agoutiで直感的に書ける
 ・ヘッドレスモードは、
  操作内容が分からないので、配布時はデフォルトオフ
 ・クローリング・スクレイピング用途でも使えそう


Slide 35

Slide 35 text

Goの得意分野が生きる ・ワンバイナリ = 配布が簡単
 ・クロスコンパイル = より多くの人(win,Mac)に配布可能


Slide 36

Slide 36 text

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


Slide 37

Slide 37 text

寄せられた苦情(おまけ) ・config.jsonってファイルが開けないんだけど(営業)
 ・win32で動かないんだけど(総務)
 →配布先のITリテラシーを考慮したい人生だった。


Slide 38

Slide 38 text

ご清聴頂きありがとうございました! Gopher道場 #3開催して頂きありがとうございました!
 @tenntennさん、メンターさんありがとうございました!