https://teideninfo.tepco.co.jp/day/teiden/index-j.html
こちらをスクレイピング をしてきましたが、仕様変更でできなくなりました。
前回、お助け頂き、データをxmlデータを取得できるようにして頂きました。
https://q.hatena.ne.jp/1586292241
その後、
各データを配列に入れてデータベースに入れようとしたけど、ちょっとうまくできません。
gets_text以外の方法がいいのかなと、色々調べているのですが、
これを機に過去のログからスクレイピング を一から勉強しなおそうと考えたけど、他の予定に追われて時間が足りないので教えて貰えないかなと思いました。
xmlに切り替えてから前のやり方で取れなくなったのかな。
方法は問わないので、教えて頂けると助かります。
一応、過去の分を今週の金曜日分までしか収集していないので、また一つ一つ収集しないといけないってことかな、、
どうぞよろしくお願いします。
コメント(26件)
5/20で走らせてみましたがエラーは出なかったのですが。
どういう状況でしょうか。
各日付でスクレイピング は問題ないです。それぞれ日付指定すれば動いています。
おそらく丸ごと日付からのテキストデータなら保存もできるでしょうが、
配列から各フィールドごとにどのようにデータベースに登録したら良いかな、という点で悩み、
前やってたようにデータ変数に入れてデータごとに入れる方がいいだろうかと試行錯誤しているところです。
[発生・復旧時間
停電地域 都道府県
[[市区町村,地区,,,]]
原因
発生軒数]
の5個の要素の配列で表していました。
https://q.hatena.ne.jp/1586292241では
発生日時,復旧日時,都県名,市区町村名|市区町村名,地区名|地区名|地区名,停電軒数,停電理由,更新日時
の8個に区切られた文字列で表していました。
データベースにはどのような内容のものをどういう順で保存しているのでしょうか
5/20のデータでやってみたらさっそく致命的なバグが見つかってしまいましたorz。
修正に少しお時間を頂戴します。
むしろ表示されている順番でそれぞれの項目ごとに保存し、日付の順番なので。公式そのままです。
の形式になるべく近づける形で書き直してみましたがどうでしょう。
def _day(d)
0 < d.size ? d.gsub(/(\d+)\/(\d+)\/(\d+) (\d\d:\d\d)/) {
sprintf("%d月%d日 %s", $2.to_i, $3.to_i, $4) } : ''
end
def tepco_xml(xml)
doc = REXML::Document.new(xml)
doc.get_elements('//データ部').map do |e|
def e.gets_text(name)
get_elements('./'+name).map {|c| c.text }.join('|')
end
def e.chiq_text(name)
get_elements('./'+name).map {|c| [c.text,
c.get_elements('../地区部/地区名').map {|d| d.text }.join('、')] }
end
[ _day(e.gets_text('発生日時'))+'~'+ # 発生日時>
_day(e.gets_text('復旧日時')), # 復旧日時>
e.gets_text('都県部/都県名'), # 都県名>
e.chiq_text('都県部/市区町村部/市区町村名'), # 市区町村名/地区名>
e.gets_text('停電軒数'), # 停電軒数>
e.gets_text('停電理由'), # 停電理由>
e.gets_text('更新日時') ] if e.has_text? # 更新日時>
end
end
tepco = tepco_xml(tepco_cache("teiden"))
tepco.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 " "
}
とりあえず試してエラー出て試行錯誤して動いています。
もう少し確認してみますが。まずはご報告まで。
今、これは指定した日(ページ)からそれぞれinfo[]で指定して取り出す・表示できるってことですよね。
今振り返ったら、これはindex1ページのみで2ヶ月分遡ることができないんですね。
前回書いていただいた分のところで、URLのindexやdayをいじるところがあったため、
繰り返し処理試していますがうまく行かない、、前提で組まれてる感じなんだけどそこを活用できないなぁ、、
を
tepco = tepco_xml(tepco_cache("teiden","day001"))
と翌日なら"day001"を翌々日なら"day002"とするだけです。
面倒なら
1.upto(60) {|num| days = "day#{'%03d' % num}"
tepco = tepco_xml(tepco_cache("teiden", days))
tepco.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 " "
}
}
とすればday001からday060まで繰り返します。
def getopt(fxml=["index"])
dgt = []
ARGV.each { |pm| dgt << pm.to_i if pm =~ /^\d+$/
Dir.glob("*-j.xml") {|f| File.delete(f) } if pm == '-new' }
return fxml if dgt.size == 0
s, l = dgt << 1
fxml.fill(1,60) {|n| "day#{'%03d'%n}"}[s,l]
end
getoptは引数を処理するためのメソッドです。
使い方はスタートと個数を指定します。
何も指定しないと ["index"]を返します。
0 10だと["index","day001","day002","day003","day004","day005","day006","day007","day008","day009"]
1 3だと["day001","day002","day003"]
という感じです。
getopt.each {|days|
tepco = tepco_xml(tepco_cache("teiden", days))
tepco.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 " "
}
}
特定の日を処理したいのならtepco_cache("teiden","day0xx")とその都度変更を加えて
一回だけ全部したいならuptoを使って
不定期にあるようであればgetoptを実装したほうが便利かなぁと思います。
def _day(d)
0 < d.size ? d.gsub(/(\d+)\/(\d+)\/(\d+) (\d\d:\d\d)/) {
sprintf("%d月%d日 %s", $2.to_i, $3.to_i, $4) } : ''
end
以下〜〜〜を書き足す所で、動くのですが、
44 tepco = tepco_xml(tepco_cache("teiden"))
45 tepco.each {|info|
46 puts info[0]
47 puts info[1]
の行で以下エラーが出ています。
:46:in `block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from file名:45:in `each'
from file名:45:in `<main>'
色々確認しましたが、ミスがあるわけでもinfoで数が足りていないわけでもなさそうで。
これに書き換える前はエラーは出ていませんでしたので。
書き換えたdef _day(d)以下で原因を探っています。
またこの辺りで、
「停電履歴情報はありません。」となるとエラーが出ますでしょうか。
やはり上記エラーが出ているようです。
5/1でも同様に確認。今はちょうど001=5/24がそれなので。
ミスがあるわけでもないので、一番右の更新時間の分のinfoがないからエラーになるのだろうか、、
またShunteiの方だとうまくいっていない感じですね。同じデザインに見えるけど何か違うのだろうか。
この場合、データが無いためnilとなっていました。
nilを削除するためにtepco_xmlのmapのendの後に.compactを追加して下さい。
e.gets_text('更新日時') ] if e.has_text? # 更新日時>
end.compact
end
Shunteiの方はどの様な点がうまくいっていない感じなのでしょうか
teidenとshunteiを同時に両方やる方法はあるのでしょうか。
5/28~5/21は『瞬時電圧低下履歴情報はありません。』ということでデータが無いので表示されません。
5/20は
tepco = tepco_xml(tepco_cache("shuntei","day008"))
tepco.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 " "
}
とすると
5月20日 14:03~
神奈川県
川崎市川崎区:
川崎市幸区:
と表示されています。
何日の分がどのようになっているか具体的に示していただけると対処がしやすいです。
一件ずつ見て、
データがないので表示されないところもエラー出ていない点も確認していたのですが、
「市」「区」までしか表示されなかったので、途中まで??と感じました。
teidenの方は右端の更新時間まで全件で表示されていたから、特にそのように思っていました。
やはり、shunteiの方だけ「市」で止まっているのか、それより右側の
「地区」「停電件数」「停電理由」「更新日時」が表示されません。
teidenの方では表示されるため、この違いはなんだろうなぁ、と確認しています。
デザインが違うのかな。htmlの方、タグが違うのだろうか、、同じに見えたのだが、、
瞬時電圧低下履歴情報を見てもらうとわかると思いますが
発生日時、都県名、市区町村名、更新日時のみです。
xmlファイルにもデータはありませんでした。
<データ部>
<発生日時>2020/05/20 14:03</発生日時>
<都県部>
<都県名>神奈川県</都県名>
<市区町村部>
<市区町村名>川崎市川崎区</市区町村名>
<市区町村名>川崎市幸区</市区町村名>
</市区町村部>
</都県部>
<更新日時>2020/05/20 14:10</更新日時>
</データ部>
ただこの回だけなのかもしれませんが。
市区町村名、地区名、停電軒数、停電理由のデータが付加されれば同じように処理されます。
更新日時は同じようにinfo[5]に入っています。
いくつか確認する中で地名があった表示もあったような、、と感じていたため、再度確認してみます。
直近で6/7とその数日前まで連日で表示されているので分かりました。
複数箇所をスクレイピング しても、「市」までしか表示されませんね。
エラーは以下が表示されました。
:56:in `<main>': undefined method `tepco_csv' for main:Object (NoMethodError)
Did you mean? tepco_cache
tepco_xml
変数をshunteiに変えただけなのに、となるとやはり公式がどこか違うということでしょうか。
上記認識とは具体的にどのような点かお知らせ下さい。
> エラーは以下が表示されました。
> :56:in `<main>': undefined method `tepco_csv' for main:Object (NoMethodError)
> Did you mean? tepco_cache
> tepco_xml
https://q.hatena.ne.jp/1586292241ではcsv形式でデータを返す方式でしたが
今回の質問でデータベースに入れる部分で上手くいかないということなので
https://q.hatena.ne.jp/1473168239でのteiden_parseと同じように配列で返すように
tepco_xmlを変えたコードを5/22にコメントしました。それ以降tepco_csvとの整合性は
取ってません。undefined methodということなので何処かで使っているのでしょうが
そのコードは間違っています。現在どのように実装しているのか公開できる部分を示して
いただけると対処方法がわかると思います。
> 複数箇所をスクレイピング しても、「市」までしか表示されませんね。
停電の場合でしょうか、瞬時電圧低下の場合でしょうか
瞬時電圧低下の場合は6/1のコメントでしたように市区町村名までです。
「市」まで表示されているものをスクレイピングすると「市」までしか表示されません。
ということでしょうか。
申し訳ありませんでした、エラーに関しては、全体を見返り見直したところ、tepco_csvを使い自分で表示する文を最後に追加していたため、それを省いたら消えエラーはなくなりました。
また、
表示されないというのは、こちらも少し勘違いしていたかもしれないのですが、
「自動復旧等による5分未満の停電」は「5分以上」と一緒にスクレイピングされていますでしょうか。ここの区分けを調べていて、瞬時電圧低下に関しては問題なく機能していました。
データ部は区分けしていないので一緒になっています。
tepco = tepco_xml(tepco_cache("teiden"))
p tepco.size
とすることで配列の個数を表示できるので一緒になっていることを確認できます。
区別する場合は
doc.get_elements('//データ部').map do |e|
の部分を
doc.get_elements('//停電表示選択[@値="5分以上の停電"]/データ部').map do |e|
や
doc.get_elements('//停電表示選択[@値="自動復旧等による5分未満の停電"]/データ部').map do |e|
とすることでどちらか一方にすることができます。
マックの方では順調に動いていて、
Windowsの方だとエラーになっていますが、恐らくRubyが更新されていないからかと手間取っています。
C:/RailsInstaller/Ruby2.1.0/lib/ruby/2.1.0/net/http.rb:920:in `connect': SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: tlsv1 alert protocol version (OpenSSL::SSL::SSLError)
検索したら同様に引っかかっている方々がおられるので、調整していますがかなり大変で。
どのVerがいいなどはありますでしょうか。
SSL_connectのエラーではないでしょうか
証明書のダウンロードと環境変数の設定はされてますでしょうか
証明書もあったのですが、
どうやらpathとVerを見たところ、Rubyを2カ所に入れているようで、とりあえず環境からまとめたいと思います。
解らなかったらまた別枠で質問させて頂きます。
当初の質問に関しては解決できたため、助かりました。
ありがとうございました。
証明書もあったのですが、
どうやらpathとVerを見たところ、Rubyを2カ所に入れているようで、とりあえず環境からまとめたいと思います。
解らなかったらまた別枠で質問させて頂きます。
当初の質問に関しては解決できたため、助かりました。
ありがとうございました。
運営に問い合わせて聞いてみたところ、
回答が0だからキャンセルされていて、回答者でなければ
報酬は払えないってことでした。
全く知らず気付きませんでした、途中で回答の方にもお願いしておけばよかった。
昔の方にポイントを送付できればよかったのだけど、できなくなっていた、、、何かの金融の規制かな。
また別で質問あげようと思うので、その時は回答の方にもちょっとコメントを残す感じでお願いします。
また今回の質問は前回の質問で解決すべきだったと思うので前回の延長と
いうことで理解していただければと思います。