libxml-ruby を使って、XBRL データを読み込む事にした。
(REXML は動作が遅いらしいので)
以前に、XBRLインスタンスファイルを指定すると、
そこからの参照ファイルの Tree 状態をコンソールに表示するものを作った。
今回、REXML -> libxml に書き換えてみた。
(実は 以前のプログラムでは、参照をすべて辿れていなかったことも気がついた)
$ cat filetree.rb
# 2009-01-10 katoy
# Show imported tree list for XBRL-instnce data.
require 'rubygems'
require 'xml/libxml'
require 'open-uri'
require 'pathname'
require 'pp'
require 'yaml'
require 'benchmark'
class FileTree
YAML_NAME = 'links.yaml'
attr_reader :links # from_url => [to_url, ...] のハッシュ
def initialize
@links = { }
# if File.exist?(YAML_NAME)
# @links = YAML.load_file(YAML_NAME)
# end
end
# def save_links
# @links.reject!{ |k, v| k.index("http") != 0 }
# File.open( YAML_NAME, 'w' ) do |f|
# YAML.dump( @links, f )
# end
# end
def generate(path, dir = '')
from_path = Utils::normalize_path(path, dir)
parent = Utils::parent(from_path)
if @links[from_path] != nil
puts "------- skip #{from_path}"
return
end
reader = nil
begin
reader = XML::Reader.file(from_path)
while 1 == reader.read
href = nil
name = "#{reader.namespace_uri}:#{reader.local_name}"
case name
when 'http://www.w3.org/2001/XMLSchema:import'
href = reader['schemaLocation']
when 'http://www.xbrl.org/2003/linkbase:schemaRef'
href = reader['xlink:href']
when 'http://www.xbrl.org/2003/linkbase:linkbaseRef'
href = reader['xlink:href']
end
if href != nil
to_path = Utils::normalize_path(href, parent)
generate(to_path, parent)
@links[from_path] = [] if @links[from_path] == nil
@links[from_path] << to_path
end
end
rescue
puts "---- error reading #{to_path}"
ensure
reader.close if reader != nil
end
end
def print_tree(path, indent=0)
return if @links[path] == nil
@links[path].each do |f|
puts "#{' ' * (4*indent)}#{f}"
print_tree(f, indent + 1)
end
end
end
class Utils
def self.cleanpath(path)
Pathname.new(path).to_s
end
def self.parent(full_path)
p = full_path.rindex('/') - 1
full_path[0..p]
end
def self.normalize_path(path, dir='')
if path.index('http://') == 0
uri = URI.parse(path)
full_path = "#{uri.scheme}://#{uri.host}#{Utils::cleanpath(uri.path.to_s)}"
elsif dir.index('http://') == 0
uri = URI.parse("#{dir}/#{path}")
full_path = "#{uri.scheme}://#{uri.host}#{Utils::cleanpath(uri.path.to_s)}"
else
path = path[5.. path.length] if path.index('file:') == 0
dir = File::expand_path('.') if dir == ''
full_path = Utils::cleanpath(File::expand_path(path, dir))
end
full_path
end
end
# See http://www.sec.gov
# SEC - adobe
# pat = "http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml"
# SEC - microsoft
# pat = "http://www.sec.gov/Archives/edgar/data/789019/000119312508215214/msft-20080930.xml"
# pat = "397-00-ConsistentInstance-valid.xbrl"
# pat = "file:/Users/youichikato/work/www/xbrl.org/XBRL-CONF/Common/instance/397-00-ConsistentInstance-valid.xbrl"
# pat = "/Users/youichikato/work/www/xbrl.org/XBRL-CONF/**/*.xbrl"
# pat = "/Users/youichikato/NetBeansProjects/ruby-xbrl/Edinet/sample/**/*.xbrl"
pat = ARGV[0]
puts Benchmark.measure {
ftree = FileTree.new
if pat.index('http:') == 0
full_path = Utils::normalize_path(pat)
ftree.generate(full_path)
ftree.print_tree(full_path)
else
Dir.glob(pat).each do |f|
full_path = Utils::normalize_path(f)
ftree.generate(full_path)
ftree.print_tree(full_path)
end
end
# ftree.save_links
}
$ ruby filetree.rb http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml
... 省略
http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829.xsd
http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_pre.xml
http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_cal.xml
http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_def.xml
http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_lab.xml
... 省略
http://www.xbrl.org/2003/xl-2003-12-31.xsd
http://www.xbrl.org/2003/xlink-2003-12-31.xsd
http://www.xbrl.org/2003/xlink-2003-12-31.xsd
4.520000 0.900000 5.420000 (371.585711)
最後の行は benchmak ライブラリーのよるものだ。ここでは 370 秒かかったことがしめされている。
参照ファイルの数(重複も)が多いことが分かる。( Tree 表示の行数は3000 行ほどになる)
また、この処理時間の遅さは、ほとんど ネットアクセスによるものとおもわれる。
(一度 読み込みをしたファイルは、再度 読み込まない工夫はしてあるが)
実用的なものにするには、次のような工夫をすることが考えられる。
1. ネット経由でアクセスしたファイルは、キャッシュフォルダーの保持し、次回からはキャッシュしたファイルへアクセスする。
(ブラウザのキャッシュフォルダと同じ考え)
2. ネット上ファイルの内容は基本的に変化しないはずなので、 ファイル上ファイルについての 解析した参照情報は
永続化保持して、再利用するようにする。
ここでは、2 番の方法を実装してみよう。
つまり @links を yaml で保持する。
ソースコード中の
initialize の変更と
ファイルの最後の save 呼び出し
を追加するだけだ。
# 実は上で示したコードでは、その部分はコメントにしてあった。
この変更を加えると 一回目の実行は遅いが、2回目はもちろん 劇的に速くなる。
$ ruby filetree.rb http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml
... 省略
4.810000 0.950000 5.760000 (382.223170)
$ ruby filetree.rb http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml
... 省略
0.050000 0.020000 0.070000 ( 0.109030)
ここでは、File からの import 情報だけを永続化して再利用したが、
XBRL の他の情報も同様の方法で永続化することも可能だろう。
XBRL データの処理の関門はいくつかある。
1. ファイルの参照を辿らないと完全な情報を得ることができない。
2. xlink, Xpointer を処理する必要がある。
でも、すべての情報を得る必要がないケースもある。
( (科目ID, コンテキスト、値) の組を得るだけなら、インスタンスファイルだけの処理で十分だ)
#インスアンスファイルを libxml をつかって処理する例は、作成済み。
# 数日中に 別途 投稿する予定である。
ファイル参照をすべて辿るのは、基本的には上に示した方法 (130 行程度) で可能なことがわかった。
次は、Xlinkm Xpointer をつかっている 各種のリンクベースファイルを処理することを行おう。
最近のコメント