У меня есть очень просто отформатированный XML-документ, который я хотел бы перевести в TSV, подходящий для импорта в Hive. Формат этого документа прост:
<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>
У меня есть работающий скрипт на Ruby, который будет правильно переводить документ, отформатированный как указано выше, в TSV. Вот здесь:
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 ГБ).
Именно здесь и вступает Hadoop. Возможно, самое простое решение - написать задание MapReduce на Java, но это не вариант, учитывая, что мне не хватает навыков Java. Поэтому я хотел написать скрипт mapper на 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.
Вторая проблема заключается в том, что любезный и полезный эксперт сказал мне, что, поскольку StreamXmlRecordReader не создает дополнительного разделителя записей, этот подход, вероятно, не сработает, и что мне нужно будет прочитать в одном строки, grep для строки, складывайте все, пока не получите / row, а затем проанализируйте его.
Это самый простой подход, и если да, то как мне лучше всего этого добиться?
Производительность не является большой проблемой, потому что эти файлы обрабатываются пакетно каждые несколько недель или около того, на случай, если это поможет.