🚅
Yahoo!鉄道運行情報をscrapeしてMastodonに投稿するBot
Yahoo!鉄道運行情報を Mastodon Bot でつぶやかせていました。
運行情報 九州【非公式】 のような感じで、日々の鉄道運行情報を私の Mastodon インスタンスでつぶやかせていました。
Nokogiri で必要な情報を取り出してくる
各エリアごとのページを開くと、一番上に事故や災害などによる運行情報が出ているものが一覧になっているので取り出してきます。
Nokogiri で doc.css(".elmTblLstLine.trouble a")
を呼び出すと、上記の行がリストで取り出されてくるので、表示行のリンク先をhref
で取り出して詳細ページのURLにアクセスして、運行情報の詳細を取り出してました。
# hrefのリンク文字列が相対リンクになっているので、ドメイン名を補っている
yahoo_uri = trouble_line.attr('href').gsub(/^/,'https://transit.yahoo.co.jp')
# Nokogiri でページにアクセスして中身を取り出している
line = Nokogiri::HTML.parse(URI.open(yahoo_uri))
name = line.css("h1.title").text
date = Time.strptime(line.css(".trouble p span").text, '(%m月%d日 %H時%M分')
text = line.css(".trouble p").text
Mastodon API で投稿する
取り出してきた運行情報が直近のものかどうかを判定した後に、Mastodon Statuses API を呼びだして実際に Bot の投稿として POST してました。
ソースコード例
#!/usr/bin/env ruby
require 'time'
require 'net/http'
require 'uri'
require 'open-uri'
require 'nokogiri'
require 'json'
robot_config = [
{
:name => "trainhokkaido", # MastodonのBotのアカウント名
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/2/"
},
{
:name => "traintouhoku",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/3/"
},
{
:name => "trainkanto",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/4/"
},
{
:name => "trainchubu",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/5/"
},
{
:name => "trainkinki",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/6/"
},
{
:name => "trainchugoku",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/8/"
},
{
:name => "trainshikoku",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/9/"
},
{
:name => "trainkyushu",
:bearer_hash => "ベアラートークン(Mastodonのアプリ登録で得られるハッシュ値)",
:yahoo_uri => "https://transit.yahoo.co.jp/traininfo/area/7/"
},
]
robot_config.each do |bot|
doc = Nokogiri::HTML.parse(URI.open(bot[:yahoo_uri]))
if doc.css(".elmTblLstLine.trouble a").length == 0 then
puts "#{Time.now} #{bot[:name]} 事故・遅延情報はありません"
else
doc.css(".elmTblLstLine.trouble a").each do |trouble_line|
yahoo_uri = trouble_line.attr('href').gsub(/^/,'https://transit.yahoo.co.jp')
line = Nokogiri::HTML.parse(URI.open(yahoo_uri))
name = line.css("h1.title").text
date = Time.strptime(line.css(".trouble p span").text, '(%m月%d日 %H時%M分')
text = line.css(".trouble p").text
puts "#{Time.now} #{bot[:name]} #{name} #{text} #{yahoo_uri}"
if Time.now - 15*60 < date then
uri = URI.parse('https://mastodon.chotto.moe/api/v1/timelines/home')
req = Net::HTTP::Get.new(uri)
req['Authorization'] = 'Bearer ' + bot[:bearer_hash]
res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: true}) do |http|
http.request(req)
end
timeline = JSON.parse(res.body, {symbolize_names: true})
if timeline.none?{ |t| t[:content].gsub(/<[^>]*>/, "") =~ /#{text}/ } then
uri = URI.parse('https://mastodon.chotto.moe/api/v1/statuses')
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer ' + bot[:bearer_hash]
req.body = "status=##{name} #{text} #traindelay #{yahoo_uri}"
res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: true}) do |http|
http.request(req)
end
puts "#{Time.now} #{bot[:name]} toot: code=#{res.code} body=#{res.body}"
end
end
end
end
doc.css(".icnNormal").each do |normal_line|
yahoo_uri = normal_line.parent.parent.css('a').attr('href').text.gsub(/^/,'https://transit.yahoo.co.jp')
line = Nokogiri::HTML.parse(URI.open(yahoo_uri))
name = line.css("h1.title").text
date = Time.strptime(line.css(".normal p span").text, '(%m月%d日 %H時%M分')
text = line.css(".normal p").text
puts "#{Time.now} #{bot[:name]} #{name} #{text} #{yahoo_uri}"
if Time.now - 15*60 < date then
uri = URI.parse('https://mastodon.chotto.moe/api/v1/timelines/home')
req = Net::HTTP::Get.new(uri)
req['Authorization'] = 'Bearer ' + bot[:bearer_hash]
res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: true}) do |http|
http.request(req)
end
timeline = JSON.parse(res.body, {symbolize_names: true})
if timeline.none?{ |t| t[:content].gsub(/<[^>]*>/, "") =~ /#{text}/ } then
uri = URI.parse('https://mastodon.chotto.moe/api/v1/statuses')
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer ' + bot[:bearer_hash]
req.body = "status=##{name} #{text} #traindelay #{yahoo_uri}"
res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: true}) do |http|
http.request(req)
end
puts "#{Time.now} #{bot[:name]} toot: code=#{res.code} body=#{res.body}"
end
end
end
end
cron で巡回させる
6年ぐらいずっとこのプログラムを10分おきに動かしてました。
Yahoo!JAPANさん、ありがとうございました。
(先ほど、このBotは全て停止したので、この記事をもって供養とします。)
Discussion