きっかけ
「リモートワークで勤怠入力が必須になってしまった...」
「いちいちブラウザ開いて入力したくない....」
ということでGolangの勉強がてらに作りました
上司に許可とって、きちんとやっている内容出していればOKとのことだったので自動化してみた感じです。
簡単な内容ですが、同僚には受けがよかったです...w
間違いなどありましたらご指摘していただけると幸いです。
ソース
前提
- Golang ver1.15
- goquery
- Agouti
- ChromeDriver ver88.0.4324.96
- zsh
- macOS ver10.15.7
注意
スクレイピングは脅威にもなります。
サイトごとに行ってよいか注意しながら行いましょう。
待ち処理などを適宜入れてください。
前準備
goファイルが走るように設定します。
% cd go_chatwork_notice/
% go mod init
% mkdir tmp/
% vi config.ini
### 記述 ###
[web]
# for Basic('user:password@')
url = https://user:password@example.com
[login]
username = your nice username
password = your nice password
[chatwork]
cwToken = your nice token
cwURL = https://xxxxxxxxxxx
cw2Me = xxxxxxxxxx
やったこと
全部説明すると長いので掻い摘みます
下記はmain.go
の内容です。
■構造体
既定の値はconfig.ini
を参照します。
値を呼ぶ時は、Scraping.UserName
のように使用します。
// 定数
const (
// TimeLayout 出力時間のフォーマット
TimeLayout string = "2006-01-02 15:04:05"
// DateLayout 日付フォーマット
DateLayout string = "2006/01/02"
)
// ScrapingList スクレイピング用のstruct
type ScrapingList struct {
URL string
UserName string
Password string
ChatworkToken string
ChatworkURL string
Chatwork2Me string
}
func init() {
config, err := ini.Load("config.ini")
if err != nil {
log.Fatalf("config.iniファイルの読み込みに失敗しました:%v", err)
}
Scraping = ScrapingList{
URL: config.Section("web").Key("url").MustString(""),
UserName: config.Section("login").Key("username").MustString(""),
Password: config.Section("login").Key("password").MustString(""),
ChatworkToken: config.Section("chatwork").Key("cwToken").MustString(""),
ChatworkURL: config.Section("chatwork").Key("cwURL").MustString(""),
Chatwork2Me: config.Section("chatwork").Key("cw2Me").MustString(""),
}
}
■API
Chatworkに結果を自身のチャットルームに通知しています。
func ChatWorkMessagePost(message string) {
values := url.Values{}
values.Add("body", message)
req, err := http.NewRequest(
"POST",
Scraping.ChatworkURL,
strings.NewReader(values.Encode()),
)
if err != nil {
log.Fatalf("リクエスト失敗しました。:%v", err)
}
// コンテントタイプを設定
req.Header.Set("X-ChatWorkToken", Scraping.ChatworkToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// メッセージ投稿
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatalf("メッセージ送信失敗しました。:%v", err)
}
defer resp.Body.Close()
// メッセージ投稿結果表示
if resp.StatusCode == http.StatusOK {
fmt.Println("メッセージ投稿に成功しました。")
} else {
fmt.Println("メッセージ投稿に失敗しました。")
}
fmt.Println("メッセージ投稿ステータスコード=[",
resp.StatusCode,
"]レスポンス内容=[",
resp.Status,
"]",
)
}
■ChromeDriver(Agouti)
ヘッドレスモードで起動しています。
ChromeOptions
で追加して快適に行うことができます。
driver := agouti.ChromeDriver(
agouti.ChromeOptions("args", []string{
"--headless",
"--window-size=1280,800",
}),
agouti.Debug,
)
err := driver.Start()
if err != nil {
errLog("Chromeドライバ起動に失敗しました。")
log.Fatalln(err)
}
defer driver.Stop()
下記では、値の入力とクリックを行います。
HTMLの要素の取得に関してはFindByXPath
やFindByName
などを使って自由に行ってください。
// ログイン処理
page.FindByXPath(`//*[@id="username"]`).Fill(Scraping.UserName)
page.FindByXPath(`//*[@id="password"]`).Fill(Scraping.Password)
page.FindByName(`login`).Click()
■Goquery
テキストの取得を行い判定をしています。
流れとしては、
- chromedriverでHTMLを取得
- HTMLをstringに変換
- goqueryでHTMLを解析して要素のテキスト取得
を行っています。
// HTMLを取得する
getAll, err := page.HTML()
if err != nil {
errLog("遷移後のHTML(一覧)取得失敗しました。")
log.Fatalln(err)
}
readerGetAll := strings.NewReader(getAll)
contentDom, _ := goquery.NewDocumentFromReader(readerGetAll)
seeText := contentDom.Find("#header > h1 > span.current-project").Text()
if seeText != "xxxxxx" {
message := "ログインに失敗しました。"
errLog(message)
log.Fatalln(message)
}
// 行数取得
rawCnt := len(contentDom.Find("#content > form:nth-child(5) > div > table > tbody > tr").Nodes)
■OS
ディレクトリの存在判定と作成し、スクリーンショット撮り保存します。
var dir = "./tmp/" + time.Now().Format(strings.Replace(DateLayout, "/", "-", -1))
// ディレクトリ作成
_, err = os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
fmt.Println("ディレクトリ作成します...")
err = os.Mkdir(dir, 0755)
if err != nil {
fmt.Println("ディレクトリ作成失敗しました...")
panic(err)
}
}
}
// スクリーンショットをとる
page.Screenshot(dir + "/input_" + time.Now().Format(TimeLayout) + ".png")
おまけ
いちいち起動したくないので、自分はcronで自動化しています。
やっていることは、
- 平日のみ起動
- 30分の揺らぎをもたす
人間らしく時間が毎日変わるので、個人的には満足してます。
% cd
% wget https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv
% crontab -e
# 記述する内容
00 18 * * 1-5 bash -c "sleep $((RANDOM \% 1800))s"; grep `date "+\%Y/\%-m/\%-d"`, syukujitsu.csv > /dev/null || cd ~/Desktop/go_kintai; bash -l -c 'go run ~/go_chatwork/main.go'
# mailコマンドで起動したか確認できます
% mail
No mail for username
今後の展開
- GoogleCalender連携
- ラズパイサーバで起動させる
- macがスリープだと、cronが走らず日報を書いてくれないので...
自動でカレンダーを見て、リモートワークの日は出勤/退勤時のメッセージ送信などをやってみようかなと思います。
これは流石に怒られるかもしれないので聞いてみます笑