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

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

上記質問にて大変助かったのですが、Rubyは動いたけどPHPは動かなかった、、残念。
上記Rubyのa-kuma3さんのご回答の続きで、集めたものをデータベースに書き込むまでがわかりません。
個別の・それぞれのデータ配列を認識して取り出す方法がわかればいいのだけど。
他の方法を使うべきかと模索してるけど、回答が見事で、もったいないから、、

スクレイピング・スパイダリングで1日1回くらい集めたデータをデータベースに保存したいのですが、どうしたらいいのやら。
とりあえずまず保存してから、ノウハウを勉強しようかと思ったのだけど、、
いきなりは難しかったか;;
MYSQLにこだわらず他のでもいいかなとか思い始めて、方向が違うなぁと参っています。
ノコギリとか使うべきなのだろうか、、、
アドバイスをお願いしたいです。

回答の条件
  • 1人20回まで
  • 13歳以上
  • 登録:2015/09/11 21:39:06
  • 終了:2015/09/18 21:40:06

ベストアンサー

id:a-kuma3 No.2

a-kuma3回答回数4365ベストアンサー獲得回数18012015/09/14 14:31:39

ポイント2500pt

前に書いた回答の rowspan 対策版を DB に書き込むようにしてみました。
DB は、SQLite を使いました。
SQLite を選んだのは、「とりあえず、手軽に始めたい」という印象を受けることと、ぼく自身が一から SQLite を入れて使ったことが無かったので、やってみたかった、というのが理由です。

まず、Ruby スクリプトがこんな感じです。

require 'open-uri'
require 'sqlite3'

# get HTML

uri = "http://teideninfo.tepco.co.jp/day/day002-j.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")



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

    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


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
}

前に書いた回答から変えたのは 2ヶ所。

  1. sqlite3 を require する
  2. treat_data メソッドを、DB に書き込むようにする



Ruby で SQLite を使えるようにするまでは、こんな手順です(ぼくがやったのは Windows です)。

  1. http://sqlite.org/download.html から、sqlite シェルと DLL をダウンロード
  2. sqlite-shell-win32-x86-???????.zip と sqlite-dll-win32-x86-???????.zip を解凍して、sqlite3.exe と sqlite3.dll を ruby の bin の場所に置く
  3. gem で sqlite3 をインストールする

gem は、こうです。

d> gem install sqlite3

# 実は、ここでちょっと はまったのですが、本筋とは関係がないので別に書きます

これで、Ruby から SQLite が使えるようになってます。


次に、先のスクリプトを使うためにテーブルを生成します。
とりあえず、データを突っ込めれば良いということで、テーブルは以下のようにしました。

create table TEPCO (
    id integer primary key,
    blackout_time text,
    recover_time text,
    pref text,
    city text,
    area text,
    blackout_count text,
    blackout_reason text,
    update_time text);

プライマリキーは AUTO INCREMENT な ID にしました。
レコードの重複のことはとりあえず考えずに、ガンガン データを追加します。

TEPCO のページでは、発生時刻と復旧時刻が同じ列に改行された表示になってましたが、これは分けておいた方が使いやすいだろうということで、二つに分けました。

残りは、TEPCO のページそのままです。


テーブルを作成するには、コマンドプロンプトから sqlite3 シェルを起動して、create table コマンドを実行します。

d> sqlite3 test.sqlite
SQLite version 3.8.11.1 2015-07-29 20:00:57
Enter ".help" for usage hints.
sqlite> create table TEPCO (id integer primary key, blackout_time text, recover_time text, pref text, city text, area text, blackout_count text, blackout_reason text, update_time text);
sqlite> .tables
TEPCO
sqlite>
sqlite> .exit

sqlite3 コマンドに続けるのが DB の実体となるファイルです。
このファイル名は、データを書き込む Ruby のスクリプトにも記述してあります。

create table でテーブルを作成し、.tables コマンドでテーブルができたことを確認。
.exit コマンドでシェルを抜けます。


これで、先に書いたスクリプトを実行する準備ができました。
実行すると、データがテーブルに書き込まれているはずです。

データの確認は sqlite3 シェルで select * ってやっても良いんですが、SQLite を使うときに便利なツールがあります。
http://sqlitebrowser.org/

他8件のコメントを見る
id:FREEz

最初からやり直してみようと、OnRailsを入れてみました。
Ruby2.1.0で、よく見たら他のところでもSqlite3の設定に必要とされていたファイルも含めて最初から入っていて、便利ぽいなぁと思いました。
ただsqlite3のインストールでエラーが出たのですが、これも入っているようで、、
試しにプログラムを稼働したら動いて書き込まれました。
たださらに試してもう一度やったら追加で同じデータが書き込まれたので、なるほど書き込み設定はしていないから当然と、sqlitebrowserで消してから再度試したら、エラーになりました。
そんなに急いで2連続で動かしてはいなかったのですが、、

C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10-x86-mingw32/
lib/sqlite3/statement.rb:108:in `step': database is locked (SQLite3::BusyExcepti
on)
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/statement.rb:108:in `block in each'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/statement.rb:107:in `loop'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/statement.rb:107:in `each'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:158:in `to_a'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:158:in `block in execute'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:95:in `prepare'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:134:in `execute'

from teiden_tepco.rb:85:in `treat_data'
from teiden_tepco.rb:107:in `block in <main>'
from teiden_tepco.rb:95:in `each_line'
from teiden_tepco.rb:95:in `<main>'

database is locked (SQLite3::BusyExcepti
とあるから、データベースが一時的に非稼働になったように感じたのですが、、
少し置いたら戻るのでしょうか、なんか動いた気もするのですが。
連続で動かした程度でエラーが出ているのか、、OnRailsの事情か、、、
もしくはsqlite3の事情なのか、、
今からでもOnRailsやめておいた方がいいでしょうか?この辺は同じですかね。
例えばMYSQLへの書き込みなどでは連続に処理でエラーなかたtので不可思議に思っているところがあります。

2015/09/17 16:13:01
id:a-kuma3

C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10-x86-mingw32/lib/sqlite3/statement.rb:108:in `step': database is locked (SQLite3::BusyException)

"database is locked" ってありますから、他のプロセスがつかんじゃってるんですね、きっと。
sqlitebrowser で削除すると、DB のファイルを更新モードでつかみっぱなしになっちゃうんだと思います。
sqlitebrowser を終了させてから実行すれば、このエラーは出ないと思います。

2015/09/17 17:20:39

その他の回答(1件)

id:pogpi No.1

pogpi回答回数356ベストアンサー獲得回数462015/09/12 07:11:41

ポイント2500pt

日時、都県毎に、tdが七つのデータがあるという仕様だとすると、
次のような感じでどうですか。

token(t1 int,name varchar(200))
hogedata(dtm datetime,t1 int,td1 varchar(2000),…,td7 varchar(2000))

主キーはtokenのt1,hogedataのdtm,t1で、hogedataのt1は外部キー制約を付けるといいでしょう。

id:FREEz

ご回答ありがとうございます。
ただどうやるのかは、正直わからないです。
いろいろ検索してみるか、、、

2015/09/13 12:03:28
id:pogpi

JRubyを使えば、jdbcでMySQLに接続できますよ。

2015/09/13 14:24:52
id:a-kuma3 No.2

a-kuma3回答回数4365ベストアンサー獲得回数18012015/09/14 14:31:39ここでベストアンサー

ポイント2500pt

前に書いた回答の rowspan 対策版を DB に書き込むようにしてみました。
DB は、SQLite を使いました。
SQLite を選んだのは、「とりあえず、手軽に始めたい」という印象を受けることと、ぼく自身が一から SQLite を入れて使ったことが無かったので、やってみたかった、というのが理由です。

まず、Ruby スクリプトがこんな感じです。

require 'open-uri'
require 'sqlite3'

# get HTML

uri = "http://teideninfo.tepco.co.jp/day/day002-j.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")



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

    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


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
}

前に書いた回答から変えたのは 2ヶ所。

  1. sqlite3 を require する
  2. treat_data メソッドを、DB に書き込むようにする



Ruby で SQLite を使えるようにするまでは、こんな手順です(ぼくがやったのは Windows です)。

  1. http://sqlite.org/download.html から、sqlite シェルと DLL をダウンロード
  2. sqlite-shell-win32-x86-???????.zip と sqlite-dll-win32-x86-???????.zip を解凍して、sqlite3.exe と sqlite3.dll を ruby の bin の場所に置く
  3. gem で sqlite3 をインストールする

gem は、こうです。

d> gem install sqlite3

# 実は、ここでちょっと はまったのですが、本筋とは関係がないので別に書きます

これで、Ruby から SQLite が使えるようになってます。


次に、先のスクリプトを使うためにテーブルを生成します。
とりあえず、データを突っ込めれば良いということで、テーブルは以下のようにしました。

create table TEPCO (
    id integer primary key,
    blackout_time text,
    recover_time text,
    pref text,
    city text,
    area text,
    blackout_count text,
    blackout_reason text,
    update_time text);

プライマリキーは AUTO INCREMENT な ID にしました。
レコードの重複のことはとりあえず考えずに、ガンガン データを追加します。

TEPCO のページでは、発生時刻と復旧時刻が同じ列に改行された表示になってましたが、これは分けておいた方が使いやすいだろうということで、二つに分けました。

残りは、TEPCO のページそのままです。


テーブルを作成するには、コマンドプロンプトから sqlite3 シェルを起動して、create table コマンドを実行します。

d> sqlite3 test.sqlite
SQLite version 3.8.11.1 2015-07-29 20:00:57
Enter ".help" for usage hints.
sqlite> create table TEPCO (id integer primary key, blackout_time text, recover_time text, pref text, city text, area text, blackout_count text, blackout_reason text, update_time text);
sqlite> .tables
TEPCO
sqlite>
sqlite> .exit

sqlite3 コマンドに続けるのが DB の実体となるファイルです。
このファイル名は、データを書き込む Ruby のスクリプトにも記述してあります。

create table でテーブルを作成し、.tables コマンドでテーブルができたことを確認。
.exit コマンドでシェルを抜けます。


これで、先に書いたスクリプトを実行する準備ができました。
実行すると、データがテーブルに書き込まれているはずです。

データの確認は sqlite3 シェルで select * ってやっても良いんですが、SQLite を使うときに便利なツールがあります。
http://sqlitebrowser.org/

他8件のコメントを見る
id:FREEz

最初からやり直してみようと、OnRailsを入れてみました。
Ruby2.1.0で、よく見たら他のところでもSqlite3の設定に必要とされていたファイルも含めて最初から入っていて、便利ぽいなぁと思いました。
ただsqlite3のインストールでエラーが出たのですが、これも入っているようで、、
試しにプログラムを稼働したら動いて書き込まれました。
たださらに試してもう一度やったら追加で同じデータが書き込まれたので、なるほど書き込み設定はしていないから当然と、sqlitebrowserで消してから再度試したら、エラーになりました。
そんなに急いで2連続で動かしてはいなかったのですが、、

C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10-x86-mingw32/
lib/sqlite3/statement.rb:108:in `step': database is locked (SQLite3::BusyExcepti
on)
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/statement.rb:108:in `block in each'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/statement.rb:107:in `loop'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/statement.rb:107:in `each'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:158:in `to_a'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:158:in `block in execute'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:95:in `prepare'

from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10

  • x86-mingw32/lib/sqlite3/database.rb:134:in `execute'

from teiden_tepco.rb:85:in `treat_data'
from teiden_tepco.rb:107:in `block in <main>'
from teiden_tepco.rb:95:in `each_line'
from teiden_tepco.rb:95:in `<main>'

database is locked (SQLite3::BusyExcepti
とあるから、データベースが一時的に非稼働になったように感じたのですが、、
少し置いたら戻るのでしょうか、なんか動いた気もするのですが。
連続で動かした程度でエラーが出ているのか、、OnRailsの事情か、、、
もしくはsqlite3の事情なのか、、
今からでもOnRailsやめておいた方がいいでしょうか?この辺は同じですかね。
例えばMYSQLへの書き込みなどでは連続に処理でエラーなかたtので不可思議に思っているところがあります。

2015/09/17 16:13:01
id:a-kuma3

C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10-x86-mingw32/lib/sqlite3/statement.rb:108:in `step': database is locked (SQLite3::BusyException)

"database is locked" ってありますから、他のプロセスがつかんじゃってるんですね、きっと。
sqlitebrowser で削除すると、DB のファイルを更新モードでつかみっぱなしになっちゃうんだと思います。
sqlitebrowser を終了させてから実行すれば、このエラーは出ないと思います。

2015/09/17 17:20:39
  • id:FREEz
    問期間を1ヶ月にしておいたはずなのに、終わった、、なんで1週間になってるんだ。
    おかげでポイント配分できなかったじゃないか、、、どうなってるんだ
    これどうにかならんのかなぁ、、配分だけでも。
    こういうところが使いづらいというか、はてなの不便だな
    そもそもアマゾンのように質問者にどうしてアンケート調査しないのだろう。
    全自動でアンケート送ればいいだけなのに、人の金なのに全自動で勝手に配分されるあたりが腹立つ、、
    アマゾンのように評価の猶予期間を設ければいいだけなのに。
    どうして金払う側の質問者から勝手にポイント取り上げるような仕組みにしているんだろう。昔から変える気がないところも苛立つけど。
    回答者は複数のサイト、他に有料の質問サイトを使っていませんかね、、無料だと回答したくないだろうし、チェックしているならそちらでもいいから教えてほしいかな。


  • id:a-kuma3
    最近は来てないけど、たまに無作為抽出でアンケートきますよ。
    # 止めちゃったような気がする

    質問者の中には、ポイントありでも投げっぱなしの自動終了&均等配分って人も多いので、自動で配分は、回答者の利便を図ってのことだと思います。
    評価の猶予という意味では、自動終了の 2日前に「自動終了しちゃうぜ」というメールが来ているはず(通知を ON にしてれば)なので、そこで気がついてよね、ということでしょう。

    ぼくもピヨちゃんも気にしてないので、ポイントを受け取る側に文句がなければ、それで良いんじゃありません? :-)


    >>
    回答者は複数のサイト、他に有料の質問サイトを使っていませんかね、、無料だと回答したくないだろうし、チェックしているならそちらでもいいから教えてほしいかな。
    <<
    ぼくは、教えて! goo と知恵袋で ID 持ってますけど、ここが一番良いかな。
    回答者と質問者がやり取りを続ける、という意味では、画面の作りは人力検索が一番良いです。
    他のふたつのサイトでは、機能が貧弱で回答者と質問者のやり取りの前提が基本 一往復という設計なので、込み入った質問に向いてません。
    画像をつけたり、文字の修飾の機能も貧弱だし、自然、回答も単純になるし、質問者も投げっぱなしになる、という流れでしょう。

    ポイントの楽天ポイントへの交換はずいぶんと前に終わったし、Amazon ギフト券への交換も ついこの前に終了したので、「ポイントが欲しい!」というのが回答の一番のインセンティブだ、という人は、もう人力検索には居ないと思いますよ。
    # いや、片手の指の数くらいは居るかな。気付いている範囲ではチョキで十分だけど :-)
  • id:FREEz
    おお参考になった。ありがとうございました。
    他のサイトの方がダメってのは、思わなかったので、、なるほど便利な点もあるのですね。
    送信、こなかったなぁ、、いつも来るからチェックしていたのだが知らずに変えてしまったのかな、、、
    個人的に、これで等分は納得がいかないので、ポイント送信しました(´・ω・`)多い分にはいいや
    24時間以内に補充できないというのが続いて、時間かかってしまった、、
    大変助かりました。 sqliteに登録する前に重複チェックするようにしてみます。
  • id:a-kuma3
    質問するときに、一度書いた質問文を修正しませんでした?

    質問の文面を書いてポイントを設定して、次に行って質問の文面を確認するページから「設定を変更する」へ移動して終了時間を設定する。
    ここで、「質問をする」ではなくて「戻る」を押して、質問の文面を修正する画面に行くと、「設定を変更する」で設定した内容(回答回数の制限とか終了日など)がデフォルトに戻されちゃいます。

    一度、地雷を踏んでみないと気が付きにくいです。
    回答回数の制限とかは質問後でも修正できるんですが、終了日は修正できないんですよね。

    後、自動終了するつもりがないなら、初期設定のポイントは 100pt にしておいた方が良いと思います。
    終了するときにがっつりと配分すれば良いのですから。


    ちなみに、ぼくはポイント以外のものももらってるので、あのままでも良かったんですけれど。
    rubygems.org の証明書の話とか、Ruby 2.2 に対応してないライブラリがあるとか、Ruby の複数バージョンで対応を変えるときのやり方とか。
    後、64bit 版の Ruby は はまりポイントが多いとか。
    こういうのがあるから、回答者は三日やると止められないとか(誰も言ってない)。
  • id:a-kuma3
    ポイント送信 受け取りました。
    少し手元に残しておけば良いのに、とか思いましたが、ありがたく頂戴します m(_ _)m

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

トラックバック

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

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

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません