🗓️

goで「今日はなんの日?」をSlackに投稿する

2023/08/12に公開

goの学習を進めたいんだけどなかなかテーマが決まらない…。
チュートリアルや本を読んでいてもなかなか頭に入らず、手を動かさないと身につかないタイプ。

そこで、自身のTwitterアカウントで自動投稿している「今日はなんの日?」をgoを使ってSlackに投稿出来るようにしてみようと思ってやってみました。
https://kids.yahoo.co.jp/today/

Yahooキッズの中に「今日はなんの日?」というコンテンツがあるので、そのページからXPATHを使って「なんの日なのか?」と「記念日の説明」を取得し、Slackに投稿するというところまでを試してみる。

YahooのページにアクセスしてHTMLを取得する

まずはページにアクセスすることが出来ないと始まらないので go を使ってHTTPアクセスを行い、ページを取得するところまでを実装してみる。

goでのHTTPアクセスは元々用意されている net/http を利用する。
そしてページのBodyの取得は ioutil を利用する。

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	log.Println("Start")
	response, error := http.Get("https://kids.yahoo.co.jp/today/")
	defer response.Body.Close()
	if error != nil {
		log.Println("Access failed.")
		return
	}

	if response.StatusCode != 200 {
		log.Fatalf("Access failed. StatusCode: %d\n", response.StatusCode)
		return
	}

	log.Println("Success access.")

	bytes, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatalln(err.Error())
		return
	}
	fmt.Println(string(bytes))
}

XPathを使ってページの要素を取得する

ページの取得は行えたので、まずはChromeを使って取得したい要素のXPathを調べる。

  • 何の日
    • //*[@id="dateDtl"]/dt/span
  • 詳細
    • //*[@id="dateDtl"]/dd

そして go で XPATH から要素を取得する方法を調べていくと、公開されているライブラリでHTMLからXPathを元に要素を取得できるものがあった。
https://github.com/antchfx/htmlquery

こちらのライブラリはURLを指定すればHTTPアクセスし、HTMLを取得、そこからXPATHを使って要素を取得するところまでやってくれるようだ。

main.go
package main

import (
	"fmt"
	"log"

	"github.com/antchfx/htmlquery"
)

func main() {
	log.Println("Start")
	doc, _ := htmlquery.LoadURL("https://kids.yahoo.co.jp/today/")

	titleElement := htmlquery.FindOne(doc, "//*[@id=\"dateDtl\"]/dt/span")
	fmt.Printf("タイトル: %s\n", htmlquery.InnerText(titleElement))

	descriptionElement := htmlquery.FindOne(doc, "//*[@id=\"dateDtl\"]/dd")
	fmt.Printf("本文: %s\n", htmlquery.InnerText(descriptionElement))
}

Slackへ投稿する

Slack周りのライブラリは slack-go を使う。
https://github.com/slack-go/slack

Slackトークン・チャンネルはハードコーディングせず、環境変数に設定する。

main.go
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/antchfx/htmlquery"
	"github.com/slack-go/slack"
)

func main() {
	log.Println("Start")
	token := os.Getenv("SLACK_BOT_TOKEN")
	channel := os.Getenv("SLACK_CHANNEL")
	if token == "" || channel == "" {
		log.Fatalln("環境変数が取得できませんでした")
		return
	}

	doc, _ := htmlquery.LoadURL("https://kids.yahoo.co.jp/today/")

	titleElement := htmlquery.FindOne(doc, "//*[@id=\"dateDtl\"]/dt/span")
	fmt.Printf("タイトル: %s\n", htmlquery.InnerText(titleElement))

	descriptionElement := htmlquery.FindOne(doc, "//*[@id=\"dateDtl\"]/dd")
	fmt.Printf("本文: %s\n", htmlquery.InnerText(descriptionElement))

	message := "✨✨✨ 今日は何の日? ✨✨✨\n\n**" + htmlquery.InnerText(titleElement) + "**\n\n" + htmlquery.InnerText(descriptionElement)
	client := slack.New(token)

	// MsgOptionText() の第二引数に true を設定すると特殊文字をエスケープする
	_, _, err := client.PostMessage(channel, slack.MsgOptionText(message, true))
	if err != nil {
		panic(err)
	}

	log.Println("Finish")
}

ここまで実装すれば go run main.go をターミナルから実行すればSlackに投稿できるようになる。

参考にしたサイト

AWSのLambdaから定期実行させる

Lambdaへの登録の方法は参考にしたサイトがあるのでこちらを見てもらうほうが良さそう。

参考にしたサイト

https://zenn.dev/simsim/articles/42cb71cec493f8

最終的なコード

main.go
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/antchfx/htmlquery"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/slack-go/slack"
)

func Handler() {
	log.Println("Start")
	token := os.Getenv("SLACK_BOT_TOKEN")
	channel := os.Getenv("SLACK_CHANNEL")
	if token == "" || channel == "" {
		log.Fatalln("環境変数が取得できませんでした")
		return
	}

	doc, _ := htmlquery.LoadURL("https://kids.yahoo.co.jp/today/")

	titleElement := htmlquery.FindOne(doc, "//*[@id=\"dateDtl\"]/dt/span")
	fmt.Printf("タイトル: %s\n", htmlquery.InnerText(titleElement))

	descriptionElement := htmlquery.FindOne(doc, "//*[@id=\"dateDtl\"]/dd")
	fmt.Printf("本文: %s\n", htmlquery.InnerText(descriptionElement))

	message := "✨✨✨ 今日は何の日? ✨✨✨\n\n" + htmlquery.InnerText(titleElement) + "\n\n" + htmlquery.InnerText(descriptionElement)
	client := slack.New(token)

	// MsgOptionText() の第二引数に true を設定すると特殊文字をエスケープする
	_, _, err := client.PostMessage(channel, slack.MsgOptionText(message, true))
	if err != nil {
		panic(err)
	}

	log.Println("Finish")
}

func main() {
	lambda.Start(Handler)
}
作成したファイルをビルドし、アップロード可能なzipファイルにする
$ GOOS=linux GOARCH=amd64 go build -o main
$ zip main.zip main

これでOK!

感想

やっぱりテーマを決めて手を動かすとさくさく進む。
何よりgoのライブラリがとても優秀でほぼライブラリを呼び出すだけでコーディングよりも調べたりする時間のほうが多かった気がする。
まだ slack-go の使い方はちゃんと調べられておらず、SlackBotの名前とアイコンの書き換え方法がわかっていないので引き続き調べていきたい。

こんな感じで今後も何かテーマを決めて学習を進めていくのが良さそう。

今回の実際のコードはこちら。
https://github.com/satoshi-nishinaka/what_day_is_today

Discussion