今回選考させて頂いた業者さんは、『Foreland Forex』です。
選考基準は、以下の通りです。
①過去のデータを開示されていること 。
②ランク付けされていること 。
③日本語であること。
【試行①】
先回紹介した方法でスクレイピングしてみた結果は、以下の通りです。
日付部分のデータが歯抜けになっているし、ランクが消えています。
このままでは、使用しづらいので別の方法を考えてみました。
【試行②】
先回紹介したサイト(勉強用メモ-WEBから情報の抜き出し(スクレイピング、スパイダリング))に記載のある別の方法を選択してみました。
選考基準は、以下の通りです。
①なるべく簡単にスクレイピング処理が可能なこと。
②Windows上で単独で作動可能なこと。
③どうせなら、マルチプラットフォームを意識した言語を学習したい。
④日本人の諸先輩方がたくさんいる(参考になるサイトがたくさんある)言語であること。
上記を考えると、①③④を満たし、EXEに加工できることから②を満たすため、[RUBY]となりました。
【設計概要】
やりたいことをまとめてみました。(以下 sp=スクレイピング)
①サイトから必要な情報をspしたい。
②spした内容を、【日付】【国名】【行事名】【ランク】【前回】【予想】【結果】としてCSVファイルに保存したい。【日付】は、【年】【月】【日】【時】【分】と分割保存したい。
③spした内容に欠落部がある場合に補正したい。
④【時】を0~23時のフォーマットにしたい。
⑤サーバータイムに変更したい。
⑥夏時間に対応させたい。
⑦バックナンバーを一括spしたい。それをまとめたい。
【コード】
上記のやりたいことを元にコードを作成してみると・・
下記は一部コード表示が化けています。コードダウンロード
#! ruby -Ks
require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'csv'
#####################################################################
#ファイルの作成
def GetDate( address ="", direct = "",gmt ="0",booldst = "false")
thday = []
dayofweek = ["日","月","火","水","木","金","土"]
cnv = CSV.readlines('Conversion.csv')
begin
#HPを開いてdocに格納
doc = Hpricot( open(address).read )
#発行日取得
daydate = (doc/"h1.heading01").inner_html
/\d+\.+\d+\.+(\d\d|\d)/ =~ daydate
daydate = $&
#発行日の分解day[0]:年 dya[1]:月 day[2]:日
day = daydate.split(/[.]/)
#桁数の統一
rescue
return false
end
2.times { |i| if day[i+1].size == 1 then day[i+1] = "0" + day[i+1] end }
#ファイル名の作成
if direct ==""
failname = "CurrentDate.csv"
else
failname = direct +"/" + day[0] + day[1] + day[2] + ".csv"
end
CSV.open(failname , "w") do |csvfail|
#docからtr要素を1行づつ抽出しtrdに格納
(doc/:tr).each do |trd|
#配列をtrdate名で作成
trdate = []
#年の格納
trdate << day[0]
#trdからth要素かつclass="eventdate"の値を抽出
trdate << (trd/"th.eventdate").inner_html.strip
#もし上記条件が否定されたら
if (trd/"th.eventdate").empty?
#上記変数から値を代入
trdate = thday.clone
else
#上記変数に値を代入
thday = trdate.clone
end
#日時の書式変更分割
/\// =~ trdate[1]
trdate << "$`" << "$'"#注意:この行の””は表示用
trdate.delete_at 1
#年の修正
if day[1] == "12" && trdate[1] == "1" then trdate[0] = trdate[0].to_i + 1 end
#trdからtd要素をtddに1行づつ抽出
(trd/:td).each do |tdd|
#trdからimg要素を抽出し、あれば"src"の値をtrdateに格納する
trdate << (tdd/:img).attr("src").strip if not (tdd/:img).empty?
#trdからtdd要素を抽出し、あればそのテキストをtrdateに格納する
trdate <= 24 then dtime = 1; dhour -= 24; end
mktimes = Time.local(trdate[0].to_i,trdate[1].to_i,trdate[2].to_i,dhour,trdate[4].to_i)
mktimes += dtime*24*60*60
#サーバータイムに変換
mktimes -= gmt.to_i*60*60
#夏時間変換
if booldst == "true" && GDST(mktimes) == true then mktimes += 60*60 end
trdate[1] = mktimes.mon
trdate[2] = mktimes.day
trdate[3] = mktimes.hour
end
end
#ランク覧の空欄に0を挿入
if trdate[7] == "" then trdate[7] = "0" end
#trdateがからでなければcsvfailに書き込み
csvfail << trdate if not trdate[0].nil?
end
end
return true
end
##########################################################################
#既存ファイルの確認(有:true 無:false)
def Selectfail( failname = "20070917.csv" ,direct = "./tmp" )
#カレントディレクトリの移動
Dir.mkdir(direct) unless FileTest.exist?(direct)
Dir.chdir(direct)
#ファイルの確認(bool型)
getf = FileTest.exist?(failname)
Dir.chdir("../")
return getf
end
###########################################################################
#既存ファイル名の成形
def namefail(no = 0)
#No3の日付作成
fday = Time.local(2007, 8, 27)
fday += 7 * no * 60 * 60 * 24
p "%04d"%fday.year + "%02d"%fday.month + "%02d"%fday.day + ".csv"
return "%04d"%fday.year + "%02d"%fday.month + "%02d"%fday.day + ".csv"
end
############################################################################
#過去のデータの取得
def HistDate(direct = "./tmp",gmt ="0",booldst = "false")
count = 3
#無限ループ
loop do
failname = namefail(count)
break if Selectfail(failname) == false
count +=1
end
if count >170 then count +=6 end
loop do
address = "http://www.foreland.co.jp/marketreport/calendar_detail_id_marketcalendar_" + count.to_s + ".html"
p address
break if GetDate(address,direct,gmt,booldst) == false && count >=180
count +=1
#sleep(3)
end
end
###########################################################################
#既存ファイルの削除
def Inzfaile(failname = "HistoricalDate.csv")
#ファイルの確認(bool型)
if FileTest.exist?(failname) then
begin
File.delete(failname)
rescue
p "古いファイルが削除できません"
return false
end
end
return true
end
##########################################################################
#ファイルの結合
def Joinfail(direct = "./tmp")
#ファイルの初期化
failname = "HistoricalDate.csv"
if Inzfaile(failname) == false then return end
#書き込み用ファイルを開く
sumdate = CSV.open(failname,"w")
#ディレクトリの移動
Dir.chdir(direct)
count =3
loop do
begin
getdate= 0
fname = namefail(count)
CSV.open(fname,"r") do |gdate|
sumdate << gdate if not gdate[0].nil?
end
count += 1
rescue
p "完了"
break
end
end
Dir.chdir("../")
sumdate.close
end
##########################################################################
#過去のデータの取得
def GetHistDate(direct = "./tmp",gmt ="0",booldst = "false")
HistDate(direct,gmt,booldst)
Joinfail(direct)
end
##########################################################################
#最新のデータを取得
def CurrentDate(gmt ="0",booldst = "false")
add ="http://www.foreland.co.jp/marketreport/calendar_detail.html"
GetDate(add,"",gmt,booldst)
end
##########################################################################
#分義
def Hab(gmt ="0",booldst = "false")
CurrentDate(gmt,booldst)
if not getf = FileTest.exist?("HistoricalDate.csv") then GetHistDate("./tmp",gmt,booldst) ; return ; end
end
##########################################################################
#夏時間
def GDST(dtime)
check = false
mon_s = 3
week_s = 2
mon_e = 11
week_e = 1
lastmonth = Time.local(dtime.year ,mon_s ,1)
lastmonth -= 24*60*60
day_s = week_s * 7 - lastmonth.wday
stime = Time.local(dtime.year , mon_s ,day_s)
lastmonth = Time.local(dtime.year ,mon_e ,1)
lastmonth -= 24*60*60
day_e = week_e * 7 - lastmonth.wday
etime = Time.local(dtime.year , mon_e , day_e)
if stime <= dtime && dtime <= etime then check = true end
return check
end
########################################################################
if ARGV[0].nil? then ARGV[0] ="0" end
if ARGV[1].nil? then ARGV[1] ="false" end
Hab(ARGV[0],ARGV[1]) 【結果】
デフォルト設定での結果は、以下の様になります。
簡単に説明すると
①《A列~E列》
日付および時間を示します。ただし、《D列(時)》の(-1)は、無指定を意味します。
つまり、時間指定がないイベントを意味しています。
②《E列》
国名を示します。※下部添付内の『Conversion.csv』にて変更可能です。
③《G列》
イベント名
④《H列》
ランクを示します。※下部添付内の『Conversion.csv』にて変更可能です。
⑤《I列~K列》
【前回】【予想】【結果】を示します。
【使い方】
①GetEventDate.zipをダウンロードします。
②解凍すると、以下のファイルがあります。
【Coversion.csv】文字変換に使用するファイルです。お好みで変更してください。
【GetEventDate.bat】タイムゾーンの変更および夏時間の有無を設定できます。
※デフォルトは、変換しないように設定してあります。
【GetEventDate.exe】起動ファイルです。
【初期設定】
【GetEventDate.bat】を右クリック→編集で開き下記の設定を行います。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
REM 引数の設定を行います。
REM ①日本標準時とサーバータイムの差を記入する。
REM サーバータイムがGMT 0 の場合は、”9”を記入してください。
REM デフォルト”0”の場合は、日本時間を示す。(タイムゾーン変換を行いません)
REM ②サマータイムの設定を行います。"true"の場合は、サマータイム変換を実施します。
REM "false"の場合は変換を行いません。
REM ③記載例: EventDate.exe 【"タイム差"】 【"サマータイムの有無"】
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
【Coversion.csv】を開きB列をお好みで変更してください。
[B1~B5]ランクの表示[B6~B17]国名表示です。
【初作動】
【GetEventDate.bat】をクリックして作動させます。exeファイルをクリックしても作動しますがその場合は、パラメーターが、デフォルト状態になります。(日本時間で夏時間なし)
【結果】
【tmp】フォルダーが成形されてその中にバックナンバーがまとめられます。
【HistoricalDate.csv】バックナンバーの結合ファイルです。
【CrrentDate.csv】今週のカレンダーです。
【次回からの作動】
初作動以外は、【HistoricalDate.csv】がある場合、【CrrentDate.csv】のみ書き換えます。
【HistoricalDate.csv】を更新したい場合は、【HistoricalDate.csv】を削除後作動させてください。
【まとめ】
本来は、夏時間の有無やGMT変換が、できるようですが、OSの壁(Windows特有のタイムゾーン表示)が原因でうまく作動しませんでした。(対応策はあるようですが・・・)そのため、地道な方法を採用しました。また、調べているときこんなフレーズを目にしました。『C++ユーザーは、rubyを覚えないほうがいいよ!・・・戻れなくなるから・・・』C++ユーザーではありませんが納得です。MQL5のAPI移植は、C++を使用しましたがもう少し早くrubyと出会えていたら、もっと簡単に加工できたと思う今日この頃です。ONZ