http://q.hatena.ne.jp/1436077068
上記質問にて大変助かったのですが、Rubyは動いたけどPHPは動かなかった、、残念。
上記Rubyのa-kuma3さんのご回答の続きで、集めたものをデータベースに書き込むまでがわかりません。
個別の・それぞれのデータ配列を認識して取り出す方法がわかればいいのだけど。
他の方法を使うべきかと模索してるけど、回答が見事で、もったいないから、、
スクレイピング・スパイダリングで1日1回くらい集めたデータをデータベースに保存したいのですが、どうしたらいいのやら。
とりあえずまず保存してから、ノウハウを勉強しようかと思ったのだけど、、
いきなりは難しかったか;;
MYSQLにこだわらず他のでもいいかなとか思い始めて、方向が違うなぁと参っています。
ノコギリとか使うべきなのだろうか、、、
アドバイスをお願いしたいです。
前に書いた回答の 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ヶ所。
Ruby で SQLite を使えるようにするまでは、こんな手順です(ぼくがやったのは Windows です)。
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/
日時、都県毎に、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は外部キー制約を付けるといいでしょう。
ご回答ありがとうございます。
ただどうやるのかは、正直わからないです。
いろいろ検索してみるか、、、
JRubyを使えば、jdbcでMySQLに接続できますよ。
前に書いた回答の 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ヶ所。
Ruby で SQLite を使えるようにするまでは、こんな手順です(ぼくがやったのは Windows です)。
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/
最初からやり直してみようと、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
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
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ので不可思議に思っているところがあります。
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 を終了させてから実行すれば、このエラーは出ないと思います。
最初からやり直してみようと、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
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/sqlite3-1.3.10
from teiden_tepco.rb:85:in `treat_data'
2015/09/17 16:13:01from 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ので不可思議に思っているところがあります。
"database is locked" ってありますから、他のプロセスがつかんじゃってるんですね、きっと。
2015/09/17 17:20:39sqlitebrowser で削除すると、DB のファイルを更新モードでつかみっぱなしになっちゃうんだと思います。
sqlitebrowser を終了させてから実行すれば、このエラーは出ないと思います。