【发布时间】:2018-12-21 20:05:13
【问题描述】:
是否有任何 gem 能够解析 XLS 和 XLSX 文件?我找到了电子表格和 ParseExcel,但它们都不懂 XLSX 格式。
【问题讨论】:
是否有任何 gem 能够解析 XLS 和 XLSX 文件?我找到了电子表格和 ParseExcel,但它们都不懂 XLSX 格式。
【问题讨论】:
我最近需要用 Ruby 解析一些 Excel 文件。丰富的库和选项结果令人困惑,所以我写了一个 blog post 来讨论它。
下表列出了不同的 Ruby 库及其支持的内容:
我有使用每个支持的库here 读取 xlsx 文件的示例代码
以下是一些使用不同库读取 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
小溪
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
以下是使用spreadsheet 库读取旧版xls 文件的示例:
电子表格
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
【讨论】:
刚刚找到 roo,它可能会完成这项工作 - 可以满足我的要求,阅读基本的电子表格。
【讨论】:
Spreadsheet 污染全局命名空间。
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
【讨论】:
Spreadsheet.class # => Module 将类重命名为“Roobook”之类的东西可以解决这个问题。不过,干得好!
我正在使用使用 nokogiri 的小溪。它很快。在我的 Macbook Air 上的 21x11250 xlsx 表上使用了 8.3 秒。让它在 ruby 1.9.3+ 上工作。每行的输出格式是行和列名到单元格内容的哈希: {"A1"=>"一个单元格", "B1"=>"另一个单元格"} 散列不保证键将按照原始列顺序。 https://github.com/pythonicrubyist/creek
dullard 是另一个使用 nokogiri 的好方法。它超级快。在我的 Macbook Air 上的 21x11250 xlsx 表上使用了 6.7 秒。让它在 ruby 2.0.0+ 上工作。每行的输出格式是一个数组: [“一个单元格”,“另一个单元格”] https://github.com/thirtyseven/dullard
刚才提到的simple_xlsx_reader 不错,就是有点慢。在我的 Macbook Air 上的 21x11250 xlsx 表上使用了 91 秒。让它在 ruby 1.9.3+ 上工作。每行的输出格式是一个数组: [“一个单元格”,“另一个单元格”] https://github.com/woahdae/simple_xlsx_reader
另一个有趣的是oxcelix。它使用 ox 的 SAX 解析器,据说比 nokogiri 的 DOM 和 SAX 解析器都快。它应该输出一个矩阵。我无法让它工作。此外,rubyzip 存在一些依赖性问题。不推荐。
总之,小溪似乎是一个不错的选择。其他帖子推荐 simple_xlsx_parser,因为它具有类似的性能。
按照建议删除了 dullard,因为它已经过时并且人们会遇到错误/遇到问题。
【讨论】:
dullard 对我来说充满了错误(非拉丁数据)。 creek 给了我需要的东西
如果您正在寻找更现代的库,请查看电子表格:http://spreadsheet.rubyforge.org/GUIDE_txt.html。 我不知道它是否支持 XLSX 文件,但考虑到它正在积极开发,我猜它支持(我不在 Windows 上,也没有 Office,所以我无法测试)。
此时,roo 似乎又是一个不错的选择。它支持 XLSX,仅通过使用 times 和单元格访问来允许(某些)迭代。我承认,虽然它并不漂亮。
此外,RubyXL 现在可以使用他们的extract_data 方法为您提供一种迭代,该方法为您提供一个二维数据数组,可以轻松地对其进行迭代。
或者,如果您尝试在 Windows 上使用 XLSX 文件,您可以使用 Ruby 的 Win32OLE 库,该库允许您与 OLE 对象进行交互,例如 Word 和 Excel 提供的对象。 但是,正如 @PanagiotisKanavos 在 cmets 中提到的那样,这有几个主要缺点:
但如果你选择使用它,你可以选择不显示 Excel,加载你的 XLSX 文件,然后通过它访问它。我不确定它是否支持迭代,但是,我认为围绕提供的方法构建不会太难,因为它是用于 Excel 的完整 Microsoft OLE API。 这是文档:http://support.microsoft.com/kb/222101 这是宝石:http://www.ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html
同样,选项看起来并没有好多少,但恐怕没有太多其他选择。很难解析作为黑盒的文件格式。而那些设法打破它的少数人并没有那么明显地做到这一点。 Google Docs 是闭源的,LibreOffice 是几千行的 harry C++。
【讨论】:
extract_data。
在过去的几周里,我一直在大量使用电子表格和 rubyXL,我必须说它们都是很棒的工具。然而,两者都受到影响的一个领域是缺乏实际实施任何有用的例子。目前我正在构建一个爬虫并使用 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"
【讨论】:
rubyXL gem 可以很好地解析 XLSX 文件。
【讨论】:
我找不到令人满意的 xlsx 解析器。 RubyXL 不做日期类型转换,Roo 尝试将数字类型转换为日期,两者在 api 和代码中都是一团糟。
所以,我写了simple_xlsx_reader。但是,您必须为 xls 使用其他东西,所以这可能不是您要寻找的完整答案。
【讨论】:
大多数在线示例(包括电子表格 gem 的作者网站)都演示了将 Excel 文件的全部内容读取到 RAM 中。如果您的电子表格很小,那很好。
xls = Spreadsheet.open(file_path)
对于处理非常大文件的任何人,更好的方法是流式读取文件的内容。电子表格 gem 支持这一点——尽管目前没有很好的记录(大约 3/2015)。
Spreadsheet.open(file_path).worksheets.first.rows do |row|
# do something with the array of CSV data
end
【讨论】:
RemoteTable library 在内部使用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"}
【讨论】: