テレビ番組をキーワード検索してSlack通知するならYahoo!テレビが最強(golang+goquery)

特定のキーワード(キャラクターや俳優やサービス等)の番組がいつ放送されるかを抑えたい!という人は結構いらっしゃるんじゃないでしょうか。
私もその一人で、自社サービスに関連する市場のニュースは抑えておきたいなぁと思い、テレビ番組をキーワード検索してSlackに流したいなと思っていました。

↓こんな感じで通知したいなぁと。

image.png

テレビ番組表のAPIやRSSはない。Yahoo!テレビを使おう。

そこで、テレビ番組表のAPIやRSSを探してみましたが、、該当するものは以下の通りでした。

  • NHK番組表API(地上波の全放送局をカバーできない)
  • Gガイド.テレビ王国(RSSがあるらしいがSo-net会員になる必要あり)
  • Livedoor RSS(2008年にサービス終了)

テレビの番組表(アニメ)取得APIについて色々というエントリーを拝見しましたが、やはりAPIやRSSで一発で地上波の番組を網羅するのは難しいようです。

そこで、スクレイピング前提でテレビ番組表のサービスを見ていたら、Yahoo!テレビ.Gガイドのキーワード検索が非常に使い勝手がよいと分かりました。

あなたが神か

https://tv.yahoo.co.jp/search/?q=ITのようにGETパラメータだけで、放送予定の番組名と番組内容からキーワード検索してくれます。
もちろん地上波の全放送局をカバー。地域設定も可能で、BS/CSの番組も検索対象に含めることができます。
goo番組表もキーワード検索できるのですが、過去の番組が多くヒットするので、今回は利用しないことにしました。

あとはスクレイピングするだけです。

golang+goqueryでスクレイピング

Yahoo!テレビ.Gガイドでは、ul.programlist > liで取得した各要素をEachすれば番組情報をさくっと取り出せます。(2018年3月時点)
今回は、golangの有名なスクレイピング用ライブラリのgoqueryを使ってみました。
jQueryと同様にセレクタを指定すれば要素を抜き出すことができます。

goqueryの基本的な使い方などはgoqueryでお手軽スクレイピング!を参照していただくとよいかと。
go getするなり何なりして利用します。

$ go get github.com/PuerkitoBio/goquery

とりあえず、以下のように動かすだけで、番組情報をテキスト化することができます。
とっても簡単です。

main.go
package main

import (
    "os"
    "fmt"
    "github.com/PuerkitoBio/goquery"
)

func main() {
    doc, err := goquery.NewDocument("https://tv.yahoo.co.jp/search/?q=キーワード")
    if err != nil {
        fmt.Print("document not found. ")
        os.Exit(1)
    }

    program := ""
    doc.Find(".programlist > li").Each(func(_ int, s *goquery.Selection) {
        program += s.Text() + "\n"
    })

    fmt.Print(program)
}

私が実際に使うときには、こんな感じに情報を絞って取り出しています。

  • 放送日時
  • 放送局
  • 番組名
  • 番組内容
  • Yahoo!テレビ内の番組詳細へのリンク
    postText := ""
    linkUrl := ""
    doc.Find(".programlist > li").Each(func(_ int, s *goquery.Selection) {
        postText += "_"
        s.Find(".leftarea > p > em").Each(func(_ int, em *goquery.Selection) {
            postText += em.Text() + " "
        })
        atag := s.Find(".rightarea > p > a").First()
        postText += s.Find(".rightarea > p > span").First().Text()
        postText += "_"
        postText += " *[" + atag.Text() + "]* :"
        linkUrl, _ = atag.Attr("href")
        postText += s.Find(".rightarea > p").Filter(":not(:has(a,span))").Text()

        postText += "\n" + "> https://tv.yahoo.co.jp/" + linkUrl + "\n"
    })

slack通知までやってみよう

このソースをマルコピでそれなりに動くかと思います。
実行時は $ go run main.go "キーワード" "https://hooks.slack.com/services/hogefuga/foobar" のように検索キーワードとwebhookのURLを指定してください。

main.go
package main

func main() {
    if len(os.Args) != 3 {
        fmt.Print("specify keyword and webhook url.")
        os.Exit(1)
    }
    var keyword string = os.Args[1]
    var webhook string = os.Args[2]

    doc, err := goquery.NewDocument("https://tv.yahoo.co.jp/search/?q=" + keyword)
    if err != nil {
        fmt.Print("document not found. ")
        os.Exit(1)
    }

    postText := ""
    linkUrl := ""
    doc.Find(".programlist > li").Each(func(_ int, s *goquery.Selection) {
        postText += "_"
        s.Find(".leftarea > p > em").Each(func(_ int, em *goquery.Selection) {
            postText += em.Text() + " "
        })
        atag := s.Find(".rightarea > p > a").First()
        postText += s.Find(".rightarea > p > span").First().Text()
        postText += "_"
        postText += " *[" + atag.Text() + "]* :"
        linkUrl, _ = atag.Attr("href")
        postText += s.Find(".rightarea > p").Filter(":not(:has(a,span))").Text()

        postText += "\n" + "> https://tv.yahoo.co.jp/" + linkUrl + "\n"
    })

    if postText == "" {
        fmt.Print("program not found. ")
        os.Exit(0)
    }

    jsonStr := "{\"text\":\"" + postText + "\"}"
    fmt.Print(jsonStr)
    req, _ := http.NewRequest(
        "POST",
        webhook,
        bytes.NewBuffer([]byte(jsonStr)),
    )
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Print(err)
    }
    defer resp.Body.Close()

}

cronで毎晩キーワード検索を仕掛けるといいんじゃないかと思います。!
以上です。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.