http://q.hatena.ne.jp/1443599941

続き。

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

回答の条件
  • 1人30回まで
  • 登録:
  • 終了:2016/03/20 01:05:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答1件)

id:a-kuma3 No.1

回答回数4974ベストアンサー獲得回数2154

ポイント200pt

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")
}

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

  • treat_data メソッドに、停電なしの判定を入れた
  • URL を指定してデータを保存する処理を extract_blackout_information メソッドに集約した
  • extract_blackout_information メソッドを複数回呼び出す
他5件のコメントを見る
id:FREEz

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

2016/02/24 17:40:11
id:a-kuma3

そちらでは例えば毎日の処理でそれぞれログ、という発想で重複しているとかは普通は気にしないものでしょうか。

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

2016/02/24 21:05:06

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません