XLS- und XLSX-Dateien (MS Excel) mit Ruby analysieren?

76

Gibt es Edelsteine, die XLS- und XLSX-Dateien analysieren können? Ich habe Spreadsheet und ParseExcel gefunden, aber beide verstehen das XLSX-Format nicht.

Daniel
quelle

Antworten:

56

Ich habe gerade Roo gefunden , das könnte den Job machen - funktioniert für meine Anforderungen und liest eine einfache Tabelle.

Chris Kimpton
quelle
12
roo funktioniert sicherlich, aber es ist frustrierend un-Ruby-artig und (für mich jedenfalls) sehr überraschend: nicht in der Lage zu sein, mit jedem über Zeilen zu iterieren? nicht in der Lage sein, über Blätter zu iterieren? eine Vorstellung von einem "Standardblatt", gefolgt vom Zugriff auf Zellen über das Arbeitsmappenobjekt?
M. Anthony Aiello
7
Es hat eine Weile gedauert , bis ich sie gefunden habe, aber diese jetzt offizielle Roo-Gabel , die Sie explizit anheften müssen, behebt meine Beschwerden über Roo. Es verfügt über #each, #to_a, einen angemessenen Blattzugriff und verschmutzt den globalen Namespace nicht, Spreadsheetindem es eine Ruby-Tabelle benötigt.
Woahdae
@woahdae Super! Es wäre toll, ein Beispiel mit diesen neuen Funktionen zu sehen. Gibt es Unterlagen? Ich bin speziell daran interessiert, jede Zeile jedes Arbeitsblatts einer Arbeitsmappe durchlaufen zu können.
Anconia
Die README dieser Gabel enthält einen zusätzlichen Abschnitt darüber, was in der Gabel neu ist. Nach der Implementierung eines xlsx-Uploads, der eine gute Typumwandlung erfordert, stellte ich jedoch fest, dass die Roo-Typumwandlung sehr zu wünschen übrig ließ. Es wurde erstickt, als versucht wurde, "2" (als Zahl formatiert) als Datum zu analysieren. Ich habe meinen eigenen Parser geschrieben, den ich viel besser mag. Ich werde ihn heute Abend auf github hochladen und mich bei Ihnen melden.
Woahdae
@woahdae Gutes Zeug. Ich freue mich auf Ihre Arbeit. Bitte senden Sie den Link, wenn Sie können.
Anconia
118

Ich musste kürzlich einige Excel-Dateien mit Ruby analysieren. Die Fülle an Bibliotheken und Optionen erwies sich als verwirrend, deshalb schrieb ich einen Blog-Beitrag darüber.

Hier ist eine Tabelle mit verschiedenen Ruby-Bibliotheken und deren Unterstützung:

Geben Sie hier die Bildbeschreibung ein

Wenn Sie Wert auf Leistung xlsxlegen, vergleichen die Bibliotheken Folgendes: Geben Sie hier die Bildbeschreibung ein

Ich habe Beispielcode zum Lesen von XLSX-Dateien mit jeder unterstützten Bibliothek hier

Hier einige Beispiele zum Lesen von xlsxDateien mit verschiedenen Bibliotheken:

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

Bach

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

Hier ist ein Beispiel für das Lesen einer Legacy- xlsDatei mithilfe der spreadsheetBibliothek:

Kalkulationstabelle

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
mattnedrich
quelle
1
Dies war ein großartiger Beitrag, den ich positiv bewertet habe, aber leider stellte ich fest, dass weder Roo noch Tabellenkalkulation mit meinen XLS-Daten funktionierten.
Guero64
2
Thnks @ guero64 Die xls-Funktionalität für roo wird tatsächlich in einem anderen Projekt namens roo-xls github.com/roo-rb/roo-xls gespeichert . Hast du diese Bibliothek ausprobiert?
Mattnedrich
4
Ich habe das Problem gefunden. Die Quelle, die die Dateien generierte, speicherte sie als XLS, aber der Inhalt war HTML. Vielen Dank für Ihre Eingabe.
Guero64
Haben Sie jemanden gefunden, der einen vernünftigen Job mit definierten Namen als Bereiche gemacht hat? ZB mit openpyxl: gist.github.com/empiricalthought/…
Steven
Großartige Vergleichsarbeit! Ich versuche, roo zu verwenden, und ich bin gewandert, ob Sie es geschafft haben, Kommentare aus einer Zelle zu extrahieren, und wie dies mit der bereitgestellten Funktion erreicht werden kann.
user1185081
43

Das Roo- Juwel eignet sich hervorragend für Excel (.xls und .xlsx) und wird aktiv weiterentwickelt.

Ich bin damit einverstanden, dass die Syntax weder großartig noch rubinartig ist. Aber das kann leicht erreicht werden mit so etwas wie:

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
Bruno Buccolo
quelle
2
Vorsicht bei dieser Namenskonvention - Spreadsheet ist eine vorhandene Konstante, die sich auf ein Modul bezieht: Das Spreadsheet.class # => ModuleUmbenennen der Klasse in "Roobook" löst dieses Problem. Tolle Arbeit!
Anconia
2
Das neueste Roo (auf der Empact-Gabel, auf die Sie zeigen) verschmutzt den Namespace nicht und wird mit #each und dergleichen geliefert. Schließlich! yay empact.
Woahdae
1
Roo Gem ist schrecklich mit großen Dateien. Das Öffnen einer 5 MB XLSx-Datei kann 30-60 Sekunden dauern, was einfach keinen Sinn ergibt.
Yura Omelchuk
1
Roo dauert lange, weil es alles in den Speicher laden muss. Es scheint auch die Tabelle in eine verwendbare Datenstruktur zu analysieren, die langsam sein kann.
Carlosfocker
Bitte wo in einem Rails-Projekt kann eine solche Datei @Bruno Buccolo aufbewahrt werden.
Wokoro Douye Samuel
26

Ich benutze Creek, der Nokiaogiri verwendet. Es ist schnell. Verwendet 8,3 Sekunden auf einem 21x11250 xlsx-Tisch auf meinem Macbook Air. Ich habe es für Ruby 1.9.3+ zum Laufen gebracht. Das Ausgabeformat für jede Zeile ist ein Hash aus Zeilen- und Spaltennamen zum Zelleninhalt: {"A1" => "eine Zelle", "B1" => "eine andere Zelle"} Der Hash übernimmt keine Garantie dafür, dass die Schlüssel vorhanden sind die ursprüngliche Spaltenreihenfolge. https://github.com/pythonicrubyist/creek

Dullard ist ein weiterer großartiger, der Nokogiri verwendet. Es ist super schnell. Verwendet 6,7 Sekunden auf einem 21x11250 xlsx-Tisch auf meinem Macbook Air. Ich habe es für Ruby 2.0.0+ zum Laufen gebracht. Das Ausgabeformat für jede Zeile ist ein Array: ["eine Zelle", "eine andere Zelle"] https://github.com/thirtyseven/dullard

Der erwähnte simple_xlsx_reader ist großartig, ein bisschen langsam. Verwendet 91 Sekunden auf einem 21x11250 xlsx-Tisch auf meinem Macbook Air. Ich habe es für Ruby 1.9.3+ zum Laufen gebracht. Das Ausgabeformat für jede Zeile ist ein Array: ["eine Zelle", "eine andere Zelle"] https://github.com/woahdae/simple_xlsx_reader

Ein weiteres interessantes ist Oxcelix. Es verwendet den SAX-Parser von ox, der angeblich schneller ist als der DOM- und SAX-Parser von Nokiaogiri. Es wird angeblich eine Matrix ausgegeben. Ich konnte es nicht zum Laufen bringen. Außerdem gab es einige Abhängigkeitsprobleme mit Rubyzip. Würde es nicht empfehlen.

Zusammenfassend scheint Creek eine gute Wahl zu sein. Andere Beiträge empfehlen simple_xlsx_parser, da es eine ähnliche Leistung hat.

Dummkopf wie empfohlen entfernt, da er veraltet ist und Leute Fehler bekommen / Probleme damit haben.

the_minted
quelle
2
Dieser Beitrag sollte Nummer eins sein
Carlosfocker
2
Ich danke Ihnen für das Teilen. Ich fand, dass das Streamen von mehr als 100.000 Zeilen aus einer XLSX-Datei mit dem Dullard-Juwel schnell und speichereffizient ist.
scarver2
1
dullardwar voller Fehler für mich (mit nicht lateinischen Daten). creekgab, was ich brauche
okliv
okliv, es wäre fantastisch, wenn Sie angeben könnten, welche Schriftsätze hier nicht mit Dullard funktionieren. Schießen Sie auch einen Beitrag zum Dullard Issue Tracker auf Github! :)
the_minted
1
In dieser Antwort wird erwähnt, dass nur 'xlsx'-Dateien gelesen werden. Was ist mit' xls'-Dateien?
anshul410
7

Wenn Sie nach moderneren Bibliotheken suchen, werfen Sie einen Blick auf die Tabelle: http://spreadsheet.rubyforge.org/GUIDE_txt.html . Ich kann nicht sagen, ob es XLSX-Dateien unterstützt, aber wenn man bedenkt, dass es aktiv entwickelt wird, schätze ich, dass dies der Fall ist (ich bin nicht unter Windows oder mit Office, daher kann ich nicht testen).

An diesem Punkt sieht es so aus, als wäre Roo wieder eine gute Option. Es unterstützt XLSX und ermöglicht (einige) Iterationen, indem es nur timesmit Zellenzugriff verwendet wird. Ich gebe zu, es ist aber nicht schön.

Außerdem kann RubyXL Ihnen jetzt mithilfe seiner extract_dataMethode eine Art Iteration geben , die Ihnen ein 2D- Datenarray liefert, über das Sie leicht iterieren können.

Wenn Sie versuchen, mit XLSX-Dateien unter Windows zu arbeiten, können Sie alternativ die Win32OLE-Bibliothek von Ruby verwenden, mit der Sie eine Schnittstelle zu OLE-Objekten herstellen können, wie sie von Word und Excel bereitgestellt werden. Wie @PanagiotisKanavos in den Kommentaren erwähnt hat, hat dies jedoch einige Hauptnachteile:

  • Excel muss installiert sein
  • Für jedes Dokument wird eine neue Excel-Instanz gestartet
  • Der Speicher- und andere Ressourcenverbrauch ist weit mehr als für eine einfache Bearbeitung von XLSX-Dokumenten erforderlich.

Wenn Sie es jedoch verwenden möchten, können Sie festlegen, dass Excel nicht angezeigt wird, dass Sie Ihre XLSX-Datei laden und über diese Datei darauf zugreifen. Ich bin mir nicht sicher, ob es Iteration unterstützt, aber ich denke nicht, dass es zu schwierig wäre, um die bereitgestellten Methoden herum aufzubauen, da es sich um die vollständige Microsoft OLE-API für Excel handelt. Hier ist die Dokumentation: http://support.microsoft.com/kb/222101 Hier ist das Juwel: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html

Auch hier sehen die Optionen nicht viel besser aus, aber ich fürchte, es gibt nicht viel anderes da draußen. Es ist schwierig, ein Dateiformat zu analysieren, das eine Black Box ist. Und die wenigen, die es geschafft haben, es zu brechen, haben es nicht so sichtbar gemacht. Google Text & Tabellen ist eine geschlossene Quelle, und LibreOffice besteht aus Tausenden von Zeilen von Harry C ++.

Linuxios
quelle
Sehr hilfreiche Infos! Ich baue gerade einen Excel-Crawler und habe Passungen dazu ( stackoverflow.com/questions/14044357/… ). Ich habe Roo aufgegeben, da die Iteration ziemlich schmerzhaft ist. Ich bin jedoch gespannt darauf, es extract_datamit RubyXL zu versuchen .
Anconia
2
Die Verwendung von OLE für XLSX ist eine schlechte Idee - XLSX ist nur komprimiertes XML mit einem bekannten Format. Es ist definitiv keine Black Box - das Open XML-Format ist sehr gut definiert, das Open XML SDK bietet alle Informationen, die zum Erstellen des XMLSX erforderlich sind XML von Hand und es gibt viele Bibliotheken, die die Arbeit mit XLSX erheblich vereinfachen.
Panagiotis Kanavos
@PanagiotisKanavos: Interessanter Punkt. Ich verstehe zwar, warum das besser wäre, aber gibt es einen Grund (wieder aus Neugier), warum die Verwendung von OLE so schlecht ist? Ich habe Windows seit einigen Jahren nicht mehr verwendet oder entwickelt, daher fehlt mir möglicherweise etwas Offensichtliches.
Linuxios
Was Sie als OLE bezeichnen, ist die Automatisierungsschnittstelle für Excel. Dazu muss Excel auf dem Server installiert sein und für jede Dateianforderung gestartet werden. Es ist langsam - jeder Anruf ist ein nicht in Bearbeitung befindlicher Anruf bei Excel. Dies ist auch gefährlich, da das Vergessen, eine Instanz zu schließen, bedeutet, dass eine Excel-Instanz im Speicher bleibt. Dies kann schnell Serverressourcen verschlingen. Tatsächlich wurde XLSX so erstellt, dass jede Anwendung eine gültige Excel-Datei erstellen kann, ohne dass Excel auf dem Server erforderlich ist. Der Overhead ist minimal - es ist nur XML-Verarbeitung
Panagiotis Kanavos
@ PanagiotisKanavos: Richtig. Ich habe vergessen, dass OLE mehr IPC als eine Bibliothek ist. Vielen Dank! Ich werde der Antwort eine Notiz hinzufügen.
Linuxios
6

Ich habe in den letzten Wochen intensiv mit Spreadsheet und rubyXL gearbeitet und ich muss sagen, dass beide großartige Tools sind. Ein Bereich, unter dem beide leiden, ist das Fehlen von Beispielen für die tatsächliche Implementierung von nützlichen Dingen. Derzeit baue ich einen Crawler und verwende rubyXL, um xlsx-Dateien und Tabellenkalkulationen für alle xls zu analysieren. Ich hoffe, der folgende Code kann als hilfreiches Beispiel dienen und zeigen, wie effektiv diese Tools sein können.

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"
Anconia
quelle
Wie iteriere ich über Zeilen? Möglich?
some_other_guy
4

Das rubyXL- Juwel analysiert XLSX-Dateien auf wundervolle Weise.

Jason Galvin
quelle
6
rubyXL (wie oben roo) wird auch seltsam, wenn Sie tatsächlich auf die Daten in einem Arbeitsblatt zugreifen. Hat das Datenmodell für eine Tabelle etwas Grundlegendes, das nicht einfach über Zeilen und Spalten iteriert werden kann?
M. Anthony Aiello
4
RubyXL ist ein Chaos. Ich empfehle es nicht.
Benzado
3

Ich konnte keinen zufriedenstellenden xlsx-Parser finden. RubyXL führt keine Datums-Typumwandlung durch, Roo hat versucht, eine Zahl als Datum zu typisieren, und beide sind sowohl in der API als auch im Code ein Chaos.

Also habe ich simple_xlsx_reader geschrieben . Sie müssten jedoch etwas anderes für xls verwenden, sodass es möglicherweise nicht die vollständige Antwort ist, nach der Sie suchen.

Woahdae
quelle
Ich freue mich darauf, es zu versuchen. Wir hoffen, später weitere Funktionen zu sehen. Toller Start!
Anconia
3

Die meisten Online-Beispiele, einschließlich der Website des Autors für das Spreadsheet-Juwel, zeigen das Lesen des gesamten Inhalts einer Excel-Datei in den Arbeitsspeicher. Das ist in Ordnung, wenn Ihre Tabelle klein ist.

xls = Spreadsheet.open(file_path)

Für alle, die mit sehr großen Dateien arbeiten, ist es besser, den Inhalt der Datei per Stream zu lesen . Das Spreadsheet-Juwel unterstützt dies - wenn auch derzeit nicht gut dokumentiert (ca. 3/2015).

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

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

scarver2
quelle
Sie müssen .each hinzufügen, wenn etwas passieren soll
Peter
2

Die RemoteTable-Bibliothek verwendet Roo intern. Es erleichtert das Lesen von Tabellenkalkulationen in verschiedenen Formaten (XLS, XLSX, CSV usw., möglicherweise entfernt, möglicherweise in einem Zip, GZ usw. gespeichert):

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

Ausgabe:

{"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"}
Seamus Abshere
quelle