Hive へのインポートに適した TSV に変換したい、非常に単純な形式の XML ドキュメントがあります。このドキュメントのフォーマットは単純です。
<root>
<row>
<ID>0</ID>
<ParentID>0</ParentID>
<Url></Url>
<Title></Title>
<Text></Text>
<Username></Username>
<Points>0</Points>
<Type>0</Type>
<Timestamp></Timestamp>
<CommentCount>0</CommentCount>
</row>
</root>
上記のようにフォーマットされたドキュメントを TSV に適切に変換する作業中の Ruby スクリプトがあります。それはここにあります:
require "rubygems"
require "crack"
xml = Crack::XML.parse(File.read("sample.xml"))
xml['root']['row'].each{ |i|
puts "#{i['ID']} #{i['ParentID']} #{i['Url']} #{i['Title']}..."
}
残念ながら、翻訳する必要があるファイルは、このスクリプトが処理できるサイズ (> 1 GB) を大幅に超えています。
ここで Hadoop の出番です。最も簡単な解決策は、おそらく Java で MapReduce ジョブを作成することですが、私には Java のスキルがないため、それは選択肢ではありません。だから私はPythonまたはRubyのいずれかでマッパースクリプトを書きたかったのですが、私は専門家とはほど遠いですが、少なくともナビゲートすることはできます.
私の計画は、次のことを行うことでした。
- StreamXmlRecordReader を使用してファイル レコードをレコードごとに解析する
- クラックを使用してデシリアライゼーションをマップする
- タブで区切られた要素の単純な逆流でそれを減らします
しかし、このアプローチは一貫して失敗しています。さまざまな Ruby/Wukong スクリプトを使用しましたが、成功しませんでした。これは、ここの記事に基づいたものです。
#!/usr/bin/env ruby
require 'rubygems'
require 'crack'
xml = nil
STDIN.each_line do |line|
puts |line|
line.strip!
if line.include?("<row")
xml = Crack::XML.parse(line)
xml['root']['row'].each{ |i|
puts "#{i['ID']} #{i['ParentID']} #{i['Url']}..."
else
puts 'no line'
end
if line.include?("</root>")
puts 'EOF'
end
end
このジョブと他のジョブは次のように失敗します。
hadoop jar /usr/lib/hadoop-0.20/contrib/streaming/hadoop-streaming-0.20.2+737.jar -input /hackernews/Datasets/sample.xml -output out -mapper mapper.rb -inputreader "StreamXmlRecordReader,begin=<row,end=</row>"
packageJobJar: [/var/lib/hadoop-0.20/cache/sog/hadoop-unjar1519776523448982201/] [] /tmp/streamjob2858887307771024146.jar tmpDir=null
11/01/14 17:29:17 INFO mapred.FileInputFormat: Total input paths to process : 1
11/01/14 17:29:17 INFO streaming.StreamJob: getLocalDirs(): [/var/lib/hadoop-0.20/cache/sog/mapred/local]
11/01/14 17:29:17 INFO streaming.StreamJob: Running job: job_201101141647_0001
11/01/14 17:29:17 INFO streaming.StreamJob: To kill this job, run:
11/01/14 17:29:17 INFO streaming.StreamJob: /usr/lib/hadoop-0.20/bin/hadoop job -Dmapred.job.tracker=localhost:8021 -kill job_201101141647_0001
11/01/14 17:29:17 INFO streaming.StreamJob: Tracking URL: http://localhost:50030/jobdetails.jsp?jobid=job_201101141647_0001
11/01/14 17:29:18 INFO streaming.StreamJob: map 0% reduce 0%
11/01/14 17:30:05 INFO streaming.StreamJob: map 100% reduce 100%
11/01/14 17:30:05 INFO streaming.StreamJob: To kill this job, run:
11/01/14 17:30:05 INFO streaming.StreamJob: /usr/lib/hadoop-0.20/bin/hadoop job -Dmapred.job.tracker=localhost:8021 -kill job_201101141647_0001
11/01/14 17:30:05 INFO streaming.StreamJob: Tracking URL: http://localhost:50030/jobdetails.jsp?jobid=job_201101141647_0001
11/01/14 17:30:05 ERROR streaming.StreamJob: Job not Successful!
11/01/14 17:30:05 INFO streaming.StreamJob: killJob...
Streaming Command Failed!
最初の問題は、どこで失敗しているのかわからないことです。自分のスクリプトなのか、StreamXmlRecordReader なのか。
2 番目の問題は、親切で親切な専門家から、StreamXmlRecordReader は追加のレコード区切り文字を生成しないため、このアプローチはおそらくうまくいかず、1 行で読み取る必要があると言われたことです。grep行の場合、/行が得られるまですべてを積み上げてから解析します。
これは最も簡単なアプローチですか? もしそうなら、どうすればそれを達成するのが最善でしょうか?
念のため、これらのファイルは数週間ごとにバッチ処理されるため、パフォーマンスは大きな問題ではありません。