LoginSignup
5
4

More than 3 years have passed since last update.

GitHub Actionsでスクレイピングを定期実行して結果をSlackに通知する

Last updated at Posted at 2021-02-20

はじめに

GitHub Actionsで処理を自動化する話です。
Go言語でスクレイピングして結果をSlackに通知します。

参考にさせていただいた記事

準備

GitHubとSlackのアカウントが必要です。
Slackはフリープランでスペースを作成し、Botから通知を送るためのチャンネルを作成しておきます。GitHubにはプライベートのリポジトリを作成しておきます(DDoS攻撃などに悪用されないため非公開にする)

流れ

以下の順番で作成を進めます。

  1. 対象ページから必要な情報を取得する処理の作成
  2. Slackアプリの作成とトークンの取得
  3. SlackにPOSTする処理の作成
  4. GitHub Actionsで処理を定時実行できるようにする

対象ページから必要な情報を取得する処理の作成

Go言語でスクレイピングする際の定番パッケージである(ように思われる)PuerkitoBio/goqueryを使用します。スクレイピングするからには有用でなおかつ毎日更新される情報が欲しいので、Yahoo!ショッピングの日替わりクーポンを取得してみます。https://topics.shopping.yahoo.co.jp/campaign/cate_coupon/index.html このURLがスクレイピングの対象です。

サンプル https://github.com/PuerkitoBio/goquery#examples を参考にしつつtitleタグの中身だけ取得する処理を作成します。

main.go
package main

import (
        "fmt"
        "log"
        "net/http"

        "github.com/PuerkitoBio/goquery"
)

func main() {
        // Request the HTML page.
        res, err := http.Get("https://topics.shopping.yahoo.co.jp/campaign/cate_coupon/index.html")
        if err != nil {
                log.Fatal(err)
        }
        defer res.Body.Close()

        if res.StatusCode != 200 {
                log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
        }

        // Load the HTML document
        doc, err := goquery.NewDocumentFromReader(res.Body)
        if err != nil {
                log.Fatal(err)
        }

        title := doc.Find("title").Text()
        fmt.Println(title)
}

ビルドして実行するとタイトルの取得結果を確認することができます。

$ go mod init
$ go build
$ ./goquery_test
【今日のクーポン】メガネ、くもり止めカテゴリで使える30%OFFクーポン

Slackアプリの作成とトークンの取得

SlackにメッセージをPOSTする処理を作成する前にSlackアプリ(Bot)を作成し、トークンを取得しておく必要があります。

  1. https://api.slack.com/apps にアクセスしてCreate New Appボタンを押下
  2. App NameDevelopment Slack Workspaceを入力してCreate Appボタンを押下
    Screenshot 2021-02-12 at 20.54.29.png

  3. 左カラムのFeatures配下にあるOAuth & Permissionsをクリック
    Screenshot 2021-02-20 at 11.09.55.png

  4. Bot Token Scopeschat:writeを追加(Add an OAuth Scopeをクリックしてリストから選択)
    Screenshot 2021-02-20 at 10.27.31.png

  5. 左カラムのSettings配下にあるInstall Appをクリック

  6. Install to Workspaceボタンを押下
    Screenshot 2021-02-20 at 11.13.01.png

7.ワークスペースにアプリのインストールが完了するとトークン(OAuth Access Token)が表示されるのでメモしておく。
Screenshot 2021-02-20 at 11.15.56 (1).png

8.メッセージを受信したいチャンネルに作成したアプリを追加しておく。ショートカットを開いてinviteと入力、表示されたリストからこのチャンネルにアプリを追加するを選択する。
Screenshot 2021-02-20 at 13.38.01.png

SlackにPOSTする処理の作成

続いてSlackにPOSTする処理を作成します。Qiitaの多くの記事にも書かれているslack-go/slackパッケージを使います。examples以下を探したところ単純にメッセージを送信するサンプルを見つけることができました。https://github.com/slack-go/slack/tree/master/examples/messages こちらをベースに作成します。

2箇所書き換えが必要です。
YOUR_TOKEN_HERE : メモしておいたOAuth Access Tokenに書き換え
CHANNEL_ID : メッセージを受信したいチャンネル名かチャンネルIDを指定

main.go
package main

import (
    "fmt"

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

func main() {
    // YOUR_TOKEN_HEREをメモしておいたトークンに置き換える
    api := slack.New("YOUR_TOKEN_HERE")
    attachment := slack.Attachment{
        Pretext: "some pretext",
        Text:    "some text",
        // Uncomment the following part to send a field too
        /*
            Fields: []slack.AttachmentField{
                slack.AttachmentField{
                    Title: "a",
                    Value: "no",
                },
            },
        */
    }

    channelID, timestamp, err := api.PostMessage(
        "CHANNEL_ID", // メッセージを送信したいチャンネルを指定する
        slack.MsgOptionText("Some text", false),
        slack.MsgOptionAttachments(attachment),
        slack.MsgOptionAsUser(true), // Add this if you want that the bot would post message as a user, otherwise it will send response using the default slackbot
    )
    if err != nil {
        fmt.Printf("%s\n", err)
        return
    }
    fmt.Printf("Message successfully sent to channel %s at %s\n", channelID, timestamp)
}

ビルドして実行するとSlackにメッセージが送信されます。

$ go mod init
$ go build
$ ./slack_bot
Message successfully sent to channel XXXXXXXXXXXXX at 1613796836.000500

Screenshot 2021-02-20 at 13.55.29 (1).png
not_in_channelとエラーが出力された場合にはチャンネルにアプリを追加して下さい。

GitHub Actionsで処理を定時実行できるようにする

まずは動作確認用のプライベートリポジトリを作成しておく。Hello, Worldを出力するだけの処理を作りgo.modも含めてリポジトリに追加しておく。

main.go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Actionsタブをクリックするとそのリポジトリに適したワークフローテンプレートがサジェストされます。サジェストされたGo言語用のテンプレートをベースにしつつワークフローを作成します。cronによる定期実行だけではなく手動で実行するためworkflow_dispatchを登録しておくと確認が楽になります。scheduleイベントはcron形式で記述できますがUTC時間なので注意が必要です。
詳しくはコチラ https://docs.github.com/ja/actions/reference/events-that-trigger-workflows

作成したワークフローファイルは、リポジトリの.github/workflowsディレクトリに登録します。ワークフローを手動実行するか設定した時刻になれば結果を確認できるはずです。ワークフローの手動実行方法 https://docs.github.com/ja/actions/managing-workflow-runs/manually-running-a-workflow

cron.yml
name: Go

on:
  workflow_dispatch:
  schedule:
    - cron: '5 15 * * *'

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2

    - name: Set up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.15

    - name: Build
      run: |
        mkdir /home/runner/work/dist
        go build -v -o /home/runner/work/dist/myapp ./...
    - name: RunMyApp
      run: /home/runner/work/dist/myapp

まとめ+α

仕上げとしてスクレイピング+Slack送信の処理を作成します。さらにトークンはシークレットから取得するようにしておきます。シークレットはリポジトリのSettingsタブのsecretsから登録する。下に掲載したサンプル(main.go)ではトークンをSLACK_TOKENにしています。

main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"

    "github.com/PuerkitoBio/goquery"
    "github.com/slack-go/slack"
)

func mustGetenv(k string) string {
    v := os.Getenv(k)
    if v == "" {
        log.Panic("env not set.")
    }
    return v
}

var token string

func init() {
    token = mustGetenv("SLACK_TOKEN")
}

func main() {
    // Request the HTML page.
    res, err := http.Get("https://topics.shopping.yahoo.co.jp/campaign/cate_coupon/index.html")
    if err != nil {
        log.Fatal(err)
    }
    defer res.Body.Close()

    if res.StatusCode != 200 {
        log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
    }

    // Load the HTML document
    doc, err := goquery.NewDocumentFromReader(res.Body)
    if err != nil {
        log.Fatal(err)
    }

    title := doc.Find("title").Text()

    api := slack.New(token)

    attachment := slack.Attachment{
        Text: "https://topics.shopping.yahoo.co.jp/campaign/cate_coupon/",
    }

    channelID, timestamp, err := api.PostMessage(
        "CHANNEL_ID",
        slack.MsgOptionText(title, false),
        slack.MsgOptionAttachments(attachment),
        slack.MsgOptionAsUser(true),
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Message successfully sent to channel %s at %s\n", channelID, timestamp)
}

ワークフローにもシークレットを取得してセットする処理を追加しておきます。(追加した前後だけ抜粋)

cron.yml
name: Go

env:
  SLACK_TOKEN: ${{secrets.SLACK_TOKEN}}

on:
  workflow_dispatch:
  schedule:
    - cron: '5 15 * * *'

処理や設定に問題がなければ以下のように毎日通知が届くようになります。
Screenshot 2021-02-20 at 22.41.39.png

最後に

GitHub Actionsで何か自動化してみたかったのでスクレイピングしてSlackに通知をしてみましたが、スクレイピングする場合にはルールとマナーを守りましょう。

5
4
0

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
5
4