web-dev-qa-db-ja.com

RubyでXLSおよびXLSX(MS Excel)ファイルを解析しますか?

XLSおよびXLSXファイルを解析できるgemはありますか? SpreadsheetとParseExcelを見つけましたが、どちらもXLSX形式を理解していません。

71
Daniel

ちょうど見つけた roo 、それは仕事をするかもしれません-基本的なスプレッドシートを読んで、私の要件に合っています。

54
Chris Kimpton

最近、いくつかのExcelファイルをRubyで解析する必要がありました。豊富なライブラリとオプションはわかりにくいため、 ブログ投稿 について書きました。

以下に、さまざまなRubyライブラリとそれらがサポートするものの表を示します。

enter image description here

パフォーマンスを重視する場合、xlsxライブラリの比較方法は次のとおりです。 enter image description here

サポートされている各ライブラリでxlsxファイルを読み取るサンプルコードがあります here

いくつかの異なるライブラリでxlsxファイルを読み取るための例を次に示します。

rubyXL

require 'rubyXL'

workbook = RubyXL::Parser.parse './sample_Excel_files/xlsx_500_rows.xlsx'
worksheets = workbook.worksheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.sheet_name}"
  num_rows = 0
  worksheet.each do |row|
    row_cells = row.cells.map{ |cell| cell.value }
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

roo

require 'roo'

workbook = Roo::Spreadsheet.open './sample_Excel_files/xlsx_500_rows.xlsx'
worksheets = workbook.sheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet}"
  num_rows = 0
  workbook.sheet(worksheet).each_row_streaming do |row|
    row_cells = row.map { |cell| cell.value }
    num_rows += 1
  end
  puts "Read #{num_rows} rows" 
end

creek

require 'creek'

workbook = Creek::Book.new './sample_Excel_files/xlsx_500_rows.xlsx'
worksheets = workbook.sheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.name}"
  num_rows = 0
  worksheet.rows.each do |row|
    row_cells = row.values
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

simple_xlsx_reader

require 'simple_xlsx_reader'

workbook = SimpleXlsxReader.open './sample_Excel_files/xlsx_500000_rows.xlsx'
worksheets = workbook.sheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.name}"
  num_rows = 0
  worksheet.rows.each do |row|
    row_cells = row
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

以下は、xlsライブラリを使用してレガシーspreadsheetファイルを読み取る例です。

スプレッドシート

require 'spreadsheet'

# Note: spreadsheet only supports .xls files (not .xlsx)
workbook = Spreadsheet.open './sample_Excel_files/xls_500_rows.xls'
worksheets = workbook.worksheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.name}"
  num_rows = 0
  worksheet.rows.each do |row|
    row_cells = row.to_a.map{ |v| v.methods.include?(:value) ? v.value : v }
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end
93
mattnedrich

roo gemはExcel(.xlsおよび.xlsx)に最適であり、積極的に開発されています。

私は構文が素晴らしいものでもRubyのようなものでもないことに同意します。ただし、次のような方法で簡単に実現できます。

class Spreadsheet
  def initialize(file_path)
    @xls = Roo::Spreadsheet.open(file_path)
  end

  def each_sheet
    @xls.sheets.each do |sheet|
      @xls.default_sheet = sheet
      yield sheet
    end
  end

  def each_row
    0.upto(@xls.last_row) do |index|
      yield @xls.row(index)
    end
  end

  def each_column
    0.upto(@xls.last_column) do |index|
      yield @xls.column(index)
    end
  end
end
42
Bruno Buccolo

ノコギリを使用したクリークを使用しています。速いです。 Macbook Airの21x11250 xlsxテーブルで8.3秒使用しました。動作するようになったRuby 1.9.3+。各行の出力形式は、セルの内容に対する行と列の名前のハッシュです:{"A1" => "a cell"、 " B1 "=>" another cell "}ハッシュは、キーが元の列の順序になることを保証しません。 https://github.com/pythonicrubyist/creek

dullardは、のこぎりを使用するもう1つのすばらしいツールです。超高速です。 Macbook Airの21x11250 xlsxテーブルで6.7秒使用しました。 Ruby 2.0.0+。各行の出力フォーマットは配列です:["a cell"、 "another cell"] https:// github .com/thirtyseven/dullard

前述のsimple_xlsx_readerは素晴らしいですが、少し遅いです。 Macbook Airの21x11250 xlsxテーブルで91秒使用しました。 Ruby 1.9.3+。各行の出力形式は配列です:["a cell"、 "another cell"] https:// github .com/woahdae/simple_xlsx_reader

もう1つの興味深いものはoxcelixです。これは、nokogiriのDOMおよびSAXパーサーよりも高速であると思われるoxのSAXパーサーを使用します。おそらくマトリックスを出力します。私はそれを動作させることができませんでした。また、rubyzipにはいくつかの依存関係の問題がありました。お勧めしません。

結論として、クリークは良い選択のようです。他の投稿では、simple_xlsx_parserが同様のパフォーマンスを持っているため、推奨しています。

推奨されているようにダラードを削除しました。古いため、エラーが発生したり、問題が発生したりします。

25
the_minted

最新のライブラリを探している場合は、スプレッドシートをご覧ください: http://spreadsheet.rubyforge.org/GUIDE_txt.html 。 XLSXファイルをサポートしているかどうかはわかりませんが、積極的に開発されていることを考えると、サポートしていると思います(WindowsやOfficeを使用していないため、テストできません)。

この時点では、 roo が適切なオプションであるように見えます。 XLSXをサポートし、セルアクセスでtimesを使用するだけで(一部)の反復が可能です。私は認めますが、それはきれいではありません。

また、RubyXLは、extract_dataメソッド。データの2次元配列を提供し、簡単に反復処理できます。

あるいは、WindowsでXLSXファイルを使用する場合は、RubyのWin32OLEライブラリを使用して、WordやExcelが提供するようなOLEオブジェクトとのインターフェースを可能にします。ただし、、@ PanagiotisKanavosがコメントで言及したように、これにはいくつかの大きな欠点があります:

  • Excelをインストールする必要があります
  • ドキュメントごとに新しいExcelインスタンスが開始されます
  • メモリやその他のリソースの消費量は、単純なXLSXドキュメントの操作に必要な量をはるかに超えています。

ただし、使用することを選択した場合は、Excelを表示せずにXLSXファイルをロードし、そのファイルからアクセスできます。反復をサポートしているかどうかはわかりませんが、完全なMicrosoft OLE Excel用APIであるため、提供されたメソッドを構築するのはそれほど難しくないと思います。ドキュメント: http://support.Microsoft.com/kb/222101 ここに宝石があります: http://www.Ruby-doc.org/stdlib-1.9.3/libdoc /win32ole/rdoc/WIN32OLE.html

繰り返しになりますが、オプションはそれほど良く見えませんが、他にそれほど多くはありません、私は恐れています。ブラックボックスのファイル形式を解析するのは困難です。そして、それを破ることができた少数の人は、目に見えてそれをしませんでした。 Google Docsはクローズドソースであり、LibreOfficeは何千行ものハリーC++です。

7
Linuxios

私はこの数週間、SpreadsheetとrubyXLの両方を頻繁に使用してきましたが、どちらも素晴らしいツールだと言わざるを得ません。ただし、両方が苦しむ1つの領域は、有用なものを実際に実装する例の欠如です。現在、クローラーを作成しており、rubyXLを使用してxlsxファイルとスプレッドシートを解析し、xlsを探しています。以下のコードが役に立つ例となり、これらのツールがどれほど効果的であるかを示すことを願っています。

require 'find'
require 'rubyXL'

count = 0

Find.find('/Users/Anconia/crawler/') do |file|             # begin iteration of each file of a specified directory
  if file =~ /\b.xlsx$\b/                                  # check if file is xlsx format
    workbook = RubyXL::Parser.parse(file).worksheets       # creates an object containing all worksheets of an Excel workbook
    workbook.each do |worksheet|                           # begin iteration over each worksheet
      data = worksheet.extract_data.to_s                   # extract data of a given worksheet - must be converted to a string in order to match a regex
      if data =~ /regex/
        puts file
        count += 1
      end      
    end
  end
end

puts "#{count} files were found"

require 'find'
require 'spreadsheet'
Spreadsheet.client_encoding = 'UTF-8'

count = 0

Find.find('/Users/Anconia/crawler/') do |file|             # begin iteration of each file of a specified directory
  if file =~ /\b.xls$\b/                                   # check if a given file is xls format
    workbook =  Spreadsheet.open(file).worksheets          # creates an object containing all worksheets of an Excel workbook
    workbook.each do |worksheet|                           # begin iteration over each worksheet
      worksheet.each do |row|                              # begin iteration over each row of a worksheet
        if row.to_s =~ /regex/                             # rows must be converted to strings in order to match the regex
          puts file
          count += 1
        end
      end
    end
  end
end

puts "#{count} files were found"
5
Anconia

rubyXL gemはXLSXファイルを美しく解析します。

4
Jason Galvin

満足できるxlsxパーサーが見つかりませんでした。 RubyXLは日付型キャストを行わず、Rooは数値を日付として型キャストしようとしましたが、どちらもAPIとコードの両方で混乱しています。

そこで、私は simple_xlsx_reader を書きました。ただし、xlsには他の何かを使用する必要があるため、探している完全な答えではない可能性があります。

3
Woahdae

Spreadsheet gemの著者のWebサイトを含むほとんどのオンライン例では、Excelファイルのコンテンツ全体をRAMに読み込む方法を示しています。スプレッドシートが小さい場合は問題ありません。

xls = Spreadsheet.open(file_path)

非常に大きなファイルを扱う人にとって、より良い方法はファイルの内容をstream-readにすることです。 Spreadsheet gemは、これをサポートしていますが、現時点では文書化されていません(2015年3月頃)。

Spreadsheet.open(file_path).worksheets.first.rows do |row|
  # do something with the array of CSV data
end

CITE: https://github.com/zdavatz/spreadsheet

3
scarver2

RemoteTableライブラリ は、内部的に roo を使用します。さまざまな形式のスプレッドシート(​​XLS、XLSX、CSVなど、リモート、場合によってはZip、gzなどに格納されている場合)を簡単に読み取ることができます。

require 'remote_table'
r = RemoteTable.new 'http://www.fueleconomy.gov/FEG/epadata/02data.Zip', :filename => 'guide_jan28.xls'
r.each do |row|
  puts row.inspect
end

出力:

{"Class"=>"TWO SEATERS", "Manufacturer"=>"ACURA", "carline name"=>"NSX", "displ"=>"3.0", "cyl"=>"6.0", "trans"=>"Auto(S4)", "drv"=>"R", "bidx"=>"60.0", "cty"=>"17.0", "hwy"=>"24.0", "cmb"=>"20.0", "ucty"=>"19.1342", "uhwy"=>"30.2", "ucmb"=>"22.9121", "fl"=>"P", "G"=>"", "T"=>"", "S"=>"", "2pv"=>"", "2lv"=>"", "4pv"=>"", "4lv"=>"", "hpv"=>"", "hlv"=>"", "fcost"=>"1238.0", "eng dscr"=>"DOHC-VTEC", "trans dscr"=>"2MODE", "vpc"=>"4.0", "cls"=>"1.0"}
{"Class"=>"TWO SEATERS", "Manufacturer"=>"ACURA", "carline name"=>"NSX", "displ"=>"3.2", "cyl"=>"6.0", "trans"=>"Manual(M6)", "drv"=>"R", "bidx"=>"65.0", "cty"=>"17.0", "hwy"=>"24.0", "cmb"=>"19.0", "ucty"=>"18.7", "uhwy"=>"30.4", "ucmb"=>"22.6171", "fl"=>"P", "G"=>"", "T"=>"", "S"=>"", "2pv"=>"", "2lv"=>"", "4pv"=>"", "4lv"=>"", "hpv"=>"", "hlv"=>"", "fcost"=>"1302.0", "eng dscr"=>"DOHC-VTEC", "trans dscr"=>"", "vpc"=>"4.0", "cls"=>"1.0"}
{"Class"=>"TWO SEATERS", "Manufacturer"=>"ASTON MARTIN", "carline name"=>"ASTON MARTIN VANQUISH", "displ"=>"5.9", "cyl"=>"12.0", "trans"=>"Auto(S6)", "drv"=>"R", "bidx"=>"1.0", "cty"=>"12.0", "hwy"=>"19.0", "cmb"=>"14.0", "ucty"=>"13.55", "uhwy"=>"24.7", "ucmb"=>"17.015", "fl"=>"P", "G"=>"G", "T"=>"", "S"=>"", "2pv"=>"", "2lv"=>"", "4pv"=>"", "4lv"=>"", "hpv"=>"", "hlv"=>"", "fcost"=>"1651.0", "eng dscr"=>"GUZZLER", "trans dscr"=>"CLKUP", "vpc"=>"4.0", "cls"=>"1.0"}
2
Seamus Abshere