上記では質問終わって無事解決できています、ありがとうございます。
例えば、今回は関西電力の停電情報です。久しぶりですが他も何とか挑戦しようかと。
http://www.kepco.co.jp/energy_supply/supply/teiden-info/index.php?ScreenName=RK00
ここで、一番右の「発生件数」を左側と合わせて抜き出して他のデータと同列にする方法はどうしたらいいでしょうか。
発生件数のリンク先URLが0から加算されているから、何かをカウントすればいいのかな、と思うところはあるのですが。当然固定ではなく変動しているのでループをどう調整しようか、、
よろしくお願いします。
前の回答のコードを、
というふうに変えてみました。
require 'open-uri' class RowData def initialize @first_line_mode_ = true @data_ = [] @rows_ = [] end def push data, rows if @first_line_mode_ then @data_ << data @rows_ << rows else i = @data_.index(nil) @data_[i] = data @rows_[i] += rows end end def clear is_remain = false (0...@data_.length).each { |i| @rows_[i] -= 1 if @rows_[i] == 0 then @data_[i] = nil else is_remain = true end } unless is_remain then @data_ = [] @rows_ = [] @first_line_mode_ = true else @first_line_mode_ = false end end def to_a @data_.dup end def empty? @data_.empty? end end def treat_data data puts data.join("\t") end # # ここまでは、前回と同じです # def extract_blackout_information(uri) # get HTML txt = "" http_options = {} open(uri, http_options){ |io| txt = io.read } data = RowData.new iter = txt.split("\n").to_enum loop do line = iter.next # 通常のデータ if line =~ %r|<td([^>]*)>(.*)</td>| then td_attr = $1 txt = $2 rowspan = 1 if td_attr =~ %r|rowspan="(\d+)"| then rowspan = $1.to_i end data.push txt, rowspan # 地区 elsif line =~ %r|<td>\s+<span class="nowrap">| then txt = "" while line =~ %r|<span class="nowrap">(.+)</span>| txt += $1 line = iter.next end data.push txt, 1 # 発生件数 elsif line =~ %r|<td rowspan="(\d+)"><a .* class="modal">| then rowspan = $1.to_i txt = "" while line !~ %r|^\s*</td>\s*$| if line =~ %r|<p class="count">(.+)</p>| then txt = $1 end line = iter.next end data.push txt, rowspan # 行の終わり elsif line =~ %r|</tr>| then unless data.empty? then treat_data data.to_a end data.clear end end end # 2016-9-6 分 extract_blackout_information("http://www.kepco.co.jp/energy_supply/supply/teiden-info/index.php?ScreenName=RK20160906")
データのバリエーションがあるので、2016年9月6日の分を解析するようにしてます(最後の行)。
こんな感じで、標準出力に表示されます
00:01~00:02 和歌山県 日高郡 日高町 大字池田 雷の影響 約-軒 00:03~02:36 和歌山県 日高郡 美浜町 大字和田 雷の影響 約290軒 00:03~02:36 和歌山県 日高郡 日高町 大字荊木、大字小池、大字小中、大字志賀、大字高家、大字萩原 雷の影響 約290軒 00:06~00:07 和歌山県 有田郡 湯浅町 大字青木、大字別所、大字湯浅 雷の影響 約-軒 00:06~00:07 和歌山県 有田郡 広川町 大字名島、大字東中、大字広 雷の影響 約-軒 00:06~00:07 和歌山県 御坊市 塩屋町 北塩屋 調査中 約-軒 00:06~00:07 和歌山県 日高郡 美浜町 大字吉原 調査中 約-軒 00:06~00:07 和歌山県 日高郡 印南町 大字印南原 調査中 約-軒 ...
前回のページとは違って、TABLE のひとつのセルに入ってる情報が HTML のソースでは複数行にまたがっているので、ループが二重になっている、というのが構造的に一番変わっているところです。
<php require 'open-uri' require 'nokogiri' def teiden_parse(html) doc = Nokogiri::HTML.parse(html) content = doc.css('#content') {'archive'=> content.css('p.archive_date').text.gsub(/ |\n|\r/, ''), 'content'=> content.css("table > tr").inject([]) {|info, tr| td = tr.css('td') if 0 < td.size if td.size == 6 # td.attribute('rowspan').value info << [td[0].text.strip, # 発生・復旧時間 td[1].text, # 停電地域(都道府県|市区町村|地区) [[td[2].text, td[3].text.gsub(/ |\n|\r/, '')]], td[4].text, # 原因 td[5].css('p.count').text] # 発生軒数 else info.at(-1)[2] << [td[0].text, td[1].text.gsub(/ |\n|\r/, '')] # 地区 end end info }} end
鋸でデータの抽出をして配列で返すようにしてみました。
停電地域の市区町村と地区が複数になることがあるようでこの部分は市区町村と地区のペアーの配列としました。
本来はrowspanを見て処理すべきですが面倒なのでtdの個数で判断するという手抜きをしています。
停電履歴情報はありません。には対応していません。
htmlがどうなっているか分かりませんがteiden_parseが[]を返えすといいのですが。
url='http://www.kepco.co.jp/energy_supply/supply/teiden-info/index.php?ScreenName=RK’+(ARGV.size == 0 ? '00' : ARGV[0])
kepco = teiden_parse(open(url).read)
p kepco['archive']
kepco['content'].each {|info|
puts info[0]
puts info[1]
puts info[2].map {|l| ' '+l[0]+':'+l[1]+"\n" }
puts info[3]
puts info[4]
puts " "
}
「件数情報」のボタンを押して表示される停電件数を、時刻とか都道府県とかとひもづけたい、ってことですか?
「件数情報」は見た目は A タグですが、クリックして表示する内容は、HTML 的にすぐ次に書いてあるので、それほど難しくないと思います。
どちらかというと、時刻や都道府県が行をぶち抜いてる(rowspan > 1)ことがある方が、処理としては面倒です。
ポイント贈ろうと思ったんですがエラーでできなくなってしまったんですね;;
おっしゃる通りで、抜き出した他のデータと同じく紐づけて保存したいという意味です。
ほぼ同じため後で調整するかもしれないですが項目や順番が違うためまずは会社ごとに別のデータベースにすべきと思いますが。
>どちらかというと、時刻や都道府県が行をぶち抜いてる(rowspan > 1)ことがある方が、処理としては面倒です。
変形というか見づらいものは入力する際はどういう処理しているのだろうと、毎回思います、、、
他に何か容易に考える方法はありますでしょうか。Mechanizeとか使えば容易ですか?
今までのを応用しようかと思うところもあったのですが、とりあえず、、まず質問してみました。。
id とか、「意味」を持たせた class がついてると多少は簡単になります。
でも、行をぶち抜いた(rowspan > 1)なところは簡単にならないので、どっちもどっちというところでしょうか。
ただ、これは個人的な感想だろうなあ、という気はします。
古くから java で XML を扱ってきた人にとっては、SAX と DOM という選択肢がありました。
- SAX : 制約が厳しいけど速い
- DOM : 自由度があるけど遅い
文字列ベースで正規表現なんかでマッチさせるやり方は SAX に近くって、Mechanize とかを使うのは DOM に当たります。
javascript とかから入った人だと、そもそも SAX って何、という感じのはずですし、なんでこんな面倒なソースを書くんだろう、という感覚を持っても不思議ではないです。