人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

http://q.hatena.ne.jp/1443599941
続き。

上記質問で大変お世話になりました。助かりました。
RubyでSqlite3に書き込む方法や分岐に関して、自分でも調べ直して、いろいろ試していますがうまくいかないので教えて頂きたいです。
1. '停電履歴情報はありません。'の際には書き込まない方にしたいのですがif分岐でエラーないのにうまくできないという、、
NULLやNILや当てはまらない逆の条件なども試したのですが。どうしても書き込まれてしまうというorz
2.URLの範囲で複数のページをそのまま認識しているのですが、この場合で短い書き方はどうすればいいのかがわかりません。
day060-j.htmlなどで、数字だけ連番で変えていきたいのですが。
Rubyにはインクリメントデクリメントがないらしいですが、for文でエラーでないのですがやはり結果として書き込まれない状態になりまして。
もしわかれば教えて頂きたいです。よろしくお願いします。


●質問者: FREEz
●カテゴリ:インターネット ウェブ制作
○ 状態 :終了
└ 回答数 : 1/1件

▽最新の回答へ

1 ● a-kuma3
●200ポイント

1. '停電履歴情報はありません。'の際には書き込まない方にしたいのですがif分岐でエラーないのにうまくできないという、、
NULLやNILや当てはまらない逆の条件なども試したのですが。どうしても書き込まれてしまうというorz

抽出のスクリプトが http://q.hatena.ne.jp/1441975146#a1251552 のコードだとして、こんな感じで大丈夫でしたけど...

def treat_data data
 if data[0] == "停電履歴情報はありません。" then
 return
 end

 db = SQLite3::Database.new("test.sqlite")
 db.transaction
 ...

Ruby のバージョンによりますが、よほど古いものを使っていなければ、スクリプトの文字コードが UTF-8 じゃないとエラーになります。

条件を判定しているところで文字を表示して、意図通りに判定できているのか、確認をしてみたら良いと思います。

def treat_data data
 if data[0] == "停電履歴情報はありません。" then
 puts "× 停電なし" # ★ここ
 return
 end
 puts "○ 停電あり" # ★ここ

 db = SQLite3::Database.new("test.sqlite")
 db.transaction
 ...


2.URLの範囲で複数のページをそのまま認識しているのですが、この場合で短い書き方はどうすればいいのかがわかりません。

ループの回し方は色々ありますが、例えば、こんな感じでいけます。

# 1 から 5 まで
(1..5).each { |i|
 uri = "http://teideninfo.tepco.co.jp/day/day#{sprintf("%03d",i)}-j.html"

 open(uri, http_options){ |io|
 txt = io.read
 }

 ...
}

頭にゼロをつけて 3ケタにするには、sprintf を使います。
http://docs.ruby-lang.org/ja/2.0.0/doc/print_format.html

当日だけは URL がちょっと変わるので、もし、当日も含めたければこんな感じ。

# 0 から 5 まで
(0..5).each { |i|
 if i == 0 then
 uri = "http://teideninfo.tepco.co.jp/day/index-j.html" # ゼロは当日
 else
 uri = "http://teideninfo.tepco.co.jp/day/day#{sprintf("%03d",i)}-j.html"
 end

 open(uri, http_options){ |io|
 txt = io.read
 }

 ...

ゼロ始まりのループだと、こういうふうにも書けます。

# 0 から 5 まで(6 までじゃない)
6.times { |i|
 ...




http://q.hatena.ne.jp/1441975146#a1251552 のコードを以下のように書き換えてみました。

require 'open-uri'
require 'sqlite3'


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
 if data[0] == "停電履歴情報はありません。" then
 return
 end

 db = SQLite3::Database.new("test.sqlite")
 db.transaction

 # 1カラム目は、<BR> で分離
 t = data[0].split /<BR>/i
 blackout_time = t[0]
 recover_time = t[1]

 # 残りはそのまま保存する
 pref = data[1]
 city = data[2]
 area = data[3]
 blackout_count = data[4]
 blackout_reason = data[5]
 update_time = data[6]

 sql = 'insert into TEPCO(blackout_time, recover_time, pref, city, area, blackout_count, blackout_reason, update_time) values (?, ?, ?, ?, ?, ?, ?, ?);'
 db.execute(sql, [blackout_time, recover_time, pref, city, area, blackout_count, blackout_reason, update_time])

 db.commit
 db.close

end


def extract_blackout_information(uri)
 # get HTML

 txt = ""
 http_options = {}

 open(uri, http_options){ |io|
 txt = io.read
 }


 # encode to UTF-8

 txt.force_encoding("Shift_JIS")
 txt = txt.encode("UTF-8")


 data = RowData.new

 txt.each_line { |line|
 line.chomp!
 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|</tr>| then
 unless data.empty? then
 treat_data data.to_a
 end
 data.clear
 end
 }
end


# 本日
extract_blackout_information("http://teideninfo.tepco.co.jp/day/index-j.html")

# 本日以前
(1..7).each { |i|
 extract_blackout_information("http://teideninfo.tepco.co.jp/day/day#{sprintf("%03d",i)}-j.html")
}

コードを移動してるので単純にテキスト比較だと随分変わっているように見えますが、それほど大きな変更はしていません。


FREEzさんのコメント
早々にお返事ありがとうございました。すぐ返事しようかと思ったのですがとりあえずまずは自分で検証したかったので時間取るためにお返事遅れました申し訳ないです。 なるほど経過を表示するとわかりやすいですね、中身がわかりやすくなって助かります。"停電履歴情報はありません。"は解決しました。 以前のソースと見比べて、そちらに追加するパターンと今回記載された一括パターンを共に試しています。 # 1 から 5 まで (1..5).each { |i| uri = "http://teideninfo.tepco.co.jp/day/day#{sprintf("%03d",i)}-j.html" open(uri, http_options){ |io| txt = io.read } 数字の当てはめ方がよくわかったのですが、これその個所に追加変更すると、どうもかっこの数か?エラーが出るなぁと調整してもダメなので今も試している最中です。今回記載頂いたプログラムがかなり助かりまして、以前のとそちらを見つつとりあえず全体から探っています。 以前、自分で繰り返し処理を試した際に小→大だけでなく大→小もできたように思うのですが、そのままできないのでなぜだろうと、基本から調べ直しました。 .reverseを使うとやり方わかったのですが以前その手法を使っていなかったので、もしかしたらうまくいっていると思い込んでいたのかもしれません、正確なやり方がわかって助かりましたw

a-kuma3さんのコメント
>> これその個所に追加変更すると、どうもかっこの数か?エラーが出るなぁと調整してもダメなので今も試している最中です。 << コメントに書いたコードだと、閉じる中括弧がひとつ足りないですね。 open メソッドのブロックで、中括弧が一対。 1..5 の Range の each メソッドのブロックで、中括弧が一対。

a-kuma3さんのコメント
括弧の対応が取れてないエラーは、未だによくやります <tt>:-)</tt> 括弧の対応を確認できるようなエディタを使うと少し楽ができます。 でも、コメント中の括弧にも反応しちゃうものが多いので、あくまでも補助。 今回の場合なんかは、 1. 動いてるコードの断片を関数化する >|ruby| def extract_blackout_information(uri) ... (動いてたコードの断片) end extract_blackout_information("http://teideninfo.tepco.co.jp/day/day002-j.html") ||< 2. ループだけを書く(これなら、括弧の対応は間違うことが少ない) >|ruby| (1..7).each { |i| } ||< 3. ループで処理したいコードを中に入れる >|ruby| (1..7).each { |i| extract_blackout_information("http://teideninfo.tepco.co.jp/day/day002-j.html") } ||< 4. ループの変数を使うように、中の処理を書き換える とやると、ミスが多少は少なくなるかな、と。

FREEzさんのコメント
なるほど、1カ所ずつ足したり消したりしていたから、ずっとエラーだったわけだw 言われて気付きましたカッコを判別するエディタのはずなのに反応していなかった、、 調べたらVer上がって仕様変わり自分で操作して調べないといけないという不便になっていた、、昔は色ですぐにわかったのに。 もうちょい昔の方を見直す必要がありますが、今回記載の方はうまくいきました。 当初、.reverse_で逆にしたならばそれを導入前には動作しないのに重複も新規で書き込まれるためこのための仕様かとためらいましたが、データベースの方への処理でupdateで解決できました。導入前にはちゃんと重複は見逃していたので驚きましたが。 しかし、データベースへの処理よりプロンプトに表示される分で高い負荷がかかるのでしょうか。処理が見れることは助かるのですがぎこちない感じに一抹の不安があります、、表示くらいならばという先入観もあるので。

a-kuma3さんのコメント
>> しかし、データベースへの処理よりプロンプトに表示される分で高い負荷がかかるのでしょうか。 << 負荷がかかる、というほどではない((他のプロセスが巻き込まれる、という感じではない))ですが、プロンプトの表示は遅いです。 ウィンドウの書き換えに時間がかかるようで、その分、本体の処理は待たされます。 例えば、左端の 1、2文字だけが見えるように、別のウィンドウでコマンドプロンプトを覆い隠すようにして実行すると、処理がザーッと流れるのが分かると思います。 コマンドプロンプトと言えど、ああいう見た目のウィンドウアプリケーションなので頻繁に表示内容を書き換えていると思うと、まあ仕方ないかな、と思います。 とはいえ、表示内容を増やすとかなり気になるくらい遅くなっちゃうので、そういうときは more のようなページャに渡すと良いです。 >|| d> ruby foo.rb | more ||< 1ウィンドウ分を表示したところで表示は止まりますが、処理は先に進んでいます(全部じゃないんですけど)。 もし、逐一 表示内容を確認する必要がなければ、ファイルにリダイレクトしちゃう。 >|| d> ruby foo.rb > foo.log ||< 末永く使おうと思ってるプログラムであれば、切り替えができるようにしておくと便利です。 >|ruby| def debug(msg) $stderr.puts msg end ... debug "メッセージ" ||< デバッグするときは、こんな感じにしておいて、いじるのが終わったら、こう変えちゃう。 >|ruby| def debug(msg) # $stderr.puts msg コメントアウトしちゃう end ||< もっときちんとやるなら、ロギング用のライブラリなんかを使います。 標準添付のものでは [http://docs.ruby-lang.org/ja/2.0.0/library/logger.html:title=Logger] とか。

FREEzさんのコメント
ログを取る、というのは面白いですね。 そちらでは例えば毎日の処理でそれぞれログ、という発想で重複しているとかは普通は気にしないものでしょうか。 表示しない分をログに渡すだと、データベースに記載する分もそちらに渡して記録するような印象で話しています。 数日おきにチェックすればいいとはいえ、本来ならちゃんと処理されたかを何かわかるように知らせる方がこういう時にはいいのかな。 どうせそっちでも確認してチェックなのだから結局同じなのですがw

a-kuma3さんのコメント
>> そちらでは例えば毎日の処理でそれぞれログ、という発想で重複しているとかは普通は気にしないものでしょうか。 << 普通のロギングのライブラリだと、メッセージ毎にレベルがあります。 だいたい、デバッグ、情報、警告、エラー、致命的、くらいな。 前の質問の回答とかではエラー処理を端折ってますけれど、エラーが起きたらエラーレベルでメッセージを残す。 「正常に終了した」だと、情報レベルでメッセージを残す。 情報レベル以下をロギングしないように設定するか、ロギングはするけど日々の確認では見ない(意識として、と、仕組みとして、のふたつあります)か、というような感じです。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ