MAGAZINE
ルーターマガジン
Ruby
Nokogiriで容量が大きいxmlをparseしてメモリ溢れ(プロセスkilled)するときのNokogiri::XML::Reader
2019.08.14
Nokogiri::XML::Readerは容量が大きいXMLファイルを扱うのに最適なクラスです。
Nokogiri::XML::Readerに至った経緯
Nokogiriで少しサイズの大きいXMLファイルをパースしたいという時に、HTMLと同じ要領で
Nokogiri::XML.parse(xml)
を手元のMacで実行できたのを確認し、Nokogiriすごい!便利!と浮かれておりましたが、
AWSのubuntu環境で実行しようとすると
$ bundle exec ruby xml_test.rb
Killed
となり、rubyプロセスが強制終了してしまいました。
Nokogiriのparseメソッドは、XMLのDOM構造をオブジェクトとして展開する為、XMLファイルのサイズによっては膨大なメモリを要するようです。
そこで、これの代わりとなる軽量なXMLパーサーは何か良いものはないかなと探してみて、辿り着いたものがNokogiri::XML::Readerです。
基本的な使い方
require 'nokogiri'
xml_str = <<'EOS'
<campany>
<!-- 会社概要 -->
<name prefix="Inc.">rooter</name>
</campany>
EOS
reader = Nokogiri::XML::Reader(xml_str)
reader.each do |node|
p node.name
end
実行結果は以下。
"campany"
"#text"
"#comment"
"#text"
"name"
"#text"
"name"
"#text"
"campany"
上のように、Nokogiri::XML::Readerに対して、ブロックを渡すことができます。
そのブロック変数は、要素1つを示すNokogiri::XML::Readerのインスタンスクラスです。
なので、このような使い方も出来ます。
p reader.first.name # "campany"
要素の種類を判別する
reader.each do |node|
p node.name
p node.node_type
end
実行結果は以下。
"campany"
1
"#text"
14
"#comment"
8
"#text"
14
"name"
1
"#text"
3
"name"
15
"#text"
14
"campany"
15
これらの数字は、以下の定数の値と対応しています。
- 開始タグ
p Nokogiri::XML::Reader::TYPE_ELEMENT == 1 #true
- 終了タグ
p Nokogiri::XML::Reader::TYPE_END_ELEMENT == 15 #true
- コメント
p Nokogiri::XML::Reader::TYPE_COMMENT == 8 #true
- タグ間の空白文字以外の文字
p Nokogiri::XML::Reader::TYPE_TEXT == 3 #true
- タグ間の空白文字
p Nokogiri::XML::Reader::TYPE_SIGNIFICANT_WHITESPACE == 14 #true
テキストフィールドの値を取得する
reader.each do |node|
p node.name
p node.value
end
実行結果は以下。
"campany"
nil
"#text"
"\n"
"#comment"
" 会社概要 "
"#text"
"\n"
"name"
nil
"#text"
"rooter"
"name"
nil
"#text"
"\n"
"campany"
nil
node_typeがTYPE_COMMENTやTYPE_TEXTではないものに対してはnilが返される。
属性を取得する
reader.each do |node|
p node.name
p node.attributes
end
実行結果は以下。
"campany"
{}
"#text"
{}
"#comment"
{}
"#text"
{}
"name"
{"prefix"=>"Inc."}
"#text"
{}
"name"
{"prefix"=>"Inc."}
"#text"
{}
"campany"
{}
属性が存在しない要素に対しては、空のハッシュが返される。
終わりに
如何でしたでしょうか。Nokogiri::XML::Readerは、Nokogiri::XML::Documentのようにオブジェクトに要素を展開せず、IOクラスにように順に読んで行くだけなので、より高速に、より軽量に処理することが出来ます。通常は、Nokogiri::XML::Documentの方が、ノード単位で扱えて便利ですが、
メモリが足りなくてDocumentクラスが取得出来ないぞ!という時に、どうぞお役立てください。
CONTACT
お問い合わせ・ご依頼はこちらから