LoginSignup
6

More than 3 years have passed since last update.

Go言語でHTMLをスクレイピングして電車の遅延情報を教えてくれるSlack-botをつくる

Last updated at Posted at 2019-05-03

GoとSlackのbotを組み合わせて勉強をしてみたかったので関東の電車運行状況を取得するbotをつくってみました。
本当は勝手につぶやく方式にしたかったのですがとりあえずこの時点で記事にします。
(そのうち改造するかも。)

Slack Appの作成

こちらのサイトを参照し作成しました。
GolangでSlack Interactive Messageを使ったBotを書く

botの作成

スクレイピングするパッケージのインストール

goqueryをインストールします。
jQuery的な操作が出来るので経験のあるWebエンジニアには割とすんなり理解できます。

$ go get github.com/PuerkitoBio/goquery

情報を取得するWebサイト

こちらがないと始まりません。
今回はYahoo!路線情報さんから取得します。

実装

では実際にコーディングしてアプリをつくります。
「運行状況」というキーワードに反応して上記サイトから運行状況を返すbotです。

slack_bot.go
package main

import (
    "log"
    "os"
    "net/url"
    "github.com/nlopes/slack"
    "github.com/PuerkitoBio/goquery"
)

// HTMLをスクレイピングして運行状況のテキストを取得する
func getRailwayText() string {
    _url := "https://transit.yahoo.co.jp/traininfo/area/4/"

    doc, err := goquery.NewDocument(_url)
    if err != nil {
        panic(err)
    }

    u := url.URL{}
    u.Scheme = doc.Url.Scheme
    u.Host = doc.Url.Host

    var message string
    // まずはテーブル要素を取得する
    doc.Find("div.trouble > table > tbody").Each(func(_ int, s *goquery.Selection) {
        // 次にtrを掘っていく
        s.Children().Each(func(idx int, ss *goquery.Selection) {
            // idx==0は見出し列なので飛ばす
            if (idx > 0) {
                railway := ss.Children().Find("a").Text()
                status := ss.Children().Find("span.colTrouble").Text()
                detail := ss.Children().Next().Next().Text()
                message = message + status + " @ " + railway + " (" + detail + ")\n"
            }
        })
    })
    return message
}

func run(api *slack.Client) int {
    rtm := api.NewRTM()
    go rtm.ManageConnection()

    for {
        select {
        case msg := <-rtm.IncomingEvents:
            switch ev := msg.Data.(type) {
            case *slack.HelloEvent:
                log.Print("Hello Event")

            case *slack.MessageEvent:
                switch ev.Text {
                case "運行状況":
                    message := getRailwayText()
                    rtm.SendMessage(rtm.NewOutgoingMessage(message, ev.Channel))
                case "help":
                    rtm.SendMessage(rtm.NewOutgoingMessage("Usage: 運行状況 関東圏の電車の運行状況を表示します。\n       他の機能?ないよ。", ev.Channel))
                }

            case *slack.InvalidAuthEvent:
                log.Print("Invalid credentials")
                return 1

            }
        }
    }
}

func main() {
    api := slack.New("YOUR TOKEN")
    os.Exit(run(api))
}

トークンはSlack APIページの左メニュー[OAuth & Permissions]の[Bot User OAuth Access Token]にあります。
スクリーンショット 2019-05-03 11.23.20.png

実行

ではローカルのターミナル上で動かしてみます。

$ go run slack_bot.go
2019/05/03 11:03:27 Hello Event

Hello Eventが表示されれば成功です。
実際につくったbotに対してメッセージを送ってみます。
スクリーンショット 2019-05-03 11.31.48.png

ちゃんと返ってきました!

終わりに

goqueryは欲しいオブジェクトの検索条件を指定するだけで勝手にとってきてくれるので非常に便利でした。
複数のサイトから情報を取得する場合であってもGoお得意の並行処理で効率的に処理したりできそうですね。
slack-botも拡張性が高く、工夫次第で様々なところで効率化ができそうです。

参考

Goとgoqueryでスクレイピング

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
6