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

More than 1 year has passed since last update.

特定のキーワード(キャラクターや俳優やサービス等)の番組がいつ放送されるかを抑えたい!という人は結構いらっしゃるんじゃないでしょうか。

私もその一人で、自社サービスに関連する市場のニュースは抑えておきたいなぁと思い、テレビ番組をキーワード検索して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で毎晩キーワード検索を仕掛けるといいんじゃないかと思います。!

以上です。