pyExcelerator - Excel-Dokument auslesen (Formatierung)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

Hallo,

hat jemand von euch Erfahrung mit dem pyExcelerator?

Das ist ein ziemlich gutes Tool für Excel2CSV und erstellen von Excel-Sheets mit Hilfe von Python.

Leider fehlt eine Dokumentation und ich Suche eine Möglichkeit, bzw die Variablen oder Funktionen um auch Formatierungen wie Farben und ähnliches auszuwerten.

Es gibt eine voregefertigte Klasse um die Zellenwerte auch Formeln und so auszulesen, aber sie gibt mir immer nur den Inhalt zurück.

Für sachdienliche Hinweise wäre ich mehr als Dankbar.

Gruss
Jay-Pi
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi und willkommen im Forum,

bist Du so nett und läßt uns ein wenig von Deinem relevanten Code sehen? Dann können wir nämlich u. U. leichter darauf kommen, wo der Fehler liegt ;-).

Gruß,
Christian
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

Folgendes liest die Zellenwerte in eine Matrix und gibt diese dann aus.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: windows-1251 -*-
# Copyright (C) 2005 Kiseliov Roman

__rev_id__ = """$Id: xls2csv.py,v 1.1 2005/10/26 07:44:24 rvk Exp $"""


from pyExcelerator import *
import sys

me, args = sys.argv[0], sys.argv[1:]


if args:
    for arg in args:
        print >>sys.stderr, 'extracting data from', arg
        for sheet_name, values in parse_xls(arg, 'cp1251'): # parse_xls(arg) -- default encoding
            matrix = [[]]
            print 'Sheet = "%s"' % sheet_name.encode('cp866', 'backslashreplace')
            print '----------------'
            for row_idx, col_idx in sorted(values.keys()):
                v = values[(row_idx, col_idx)]
                if isinstance(v, unicode):
                    v = v.encode('cp866', 'backslashreplace')
                else:
                    v = `v`
                v = '"%s"' % v.strip()
                last_row, last_col = len(matrix), len(matrix[-1])
                while last_row <= row_idx:
                    matrix.extend([[]])
                    last_row = len(matrix)

                while last_col < col_idx:
                    matrix[-1].extend([''])
                    last_col = len(matrix[-1])

                matrix[-1].extend([v])
                    
            for row in matrix:
                csv_row = ', '.join(row)
                print csv_row

else:
    print 'usage: %s (inputfile)+' % me

Die verwendete Funktion Hauptteil sieht etwa so aus:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: windows-1251 -*-

#  Copyright (C) 2005 Roman V. Kiseliov
#  All rights reserved.

# ...

    import struct

    encodings = {
        0x016F: 'ascii',     #ASCII
        0x01B5: 'cp437',     #IBM PC CP-437 (US)
        0x02D0: 'cp720',     #IBM PC CP-720 (OEM Arabic)
        0x02E1: 'cp737',     #IBM PC CP-737 (Greek)
        #...
    }

    biff8 = True
    SST = {}
    sheets = []
    sheet_names = []
    values = {}
    ws_num = 0
    BOFs = 0
    EOFs = 0

    # Inside MS Office document looks like filesystem
    # We need extract stream named 'Workbook' or 'Book'
    ole_streams = CompoundDoc.Reader(filename).STREAMS

    if 'Workbook' in ole_streams:
        workbook_stream = ole_streams['Workbook']
    elif 'Book' in ole_streams:
        workbook_stream = ole_streams['Book']
    else:
        raise Exception, 'No workbook stream in file.'

    workbook_stream_len = len(workbook_stream)
    stream_pos = 0
    
    # Excel's method of data storing is based on 
    # ancient technology "TLV" (Type, Length, Value).
    # In addition, if record size grows to some limit
    # Excel writes CONTINUE records
    while stream_pos < workbook_stream_len and EOFs <= ws_num:
        rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
        stream_pos += 4
        
        rec_data = workbook_stream[stream_pos:stream_pos+data_size]
        stream_pos += data_size

        if rec_id == 0x0809: # BOF
            #print 'BOF', 
            BOFs += 1
            ver, substream_type = unpack('<2H', rec_data[:4])
            if substream_type == 0x0005:
                # workbook global substream
                biff8 = ver >= 0x0600
            elif substream_type == 0x0010:
                # worksheet substream
                pass
            else: # skip chart stream or unknown stream
            # stream offsets may be used from BOUNDSHEET record
                rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
                while rec_id != 0x000A: # EOF
                    #print 'SST CONTINUE'
                    stream_pos += 4
                    stream_pos += data_size
                    rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
            #print 'BIFF8 == ', biff8
        elif rec_id == 0x000A: # EOF
            #print 'EOF'
            if BOFs > 1:
                sheets.extend([values])
                values = {}
            EOFs += 1
        elif rec_id == 0x0042: # CODEPAGE
            cp ,  = unpack('<H', rec_data)
            #print 'CODEPAGE', hex(cp)
            if not encoding:
                encoding = encodings[cp]
            #print encoding
        elif rec_id == 0x0085: # BOUNDSHEET
            #print 'BOUNDSHEET',
            ws_num += 1
            b = process_BOUNDSHEET(biff8, rec_data)
            sheet_names.extend([b])
            #print b.encode('cp866')
        elif rec_id == 0x00FC: # SST
            #print 'SST'
            sst_data = rec_data
            sst_continues = []
            rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
            while rec_id == 0x003C: # CONTINUE
                #print 'SST CONTINUE'
                stream_pos += 4
                rec_data = workbook_stream[stream_pos:stream_pos+data_size]
                sst_continues.extend([rec_data])
                stream_pos += data_size
                rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
            SST = process_SST(sst_data, sst_continues)
        elif rec_id == 0x00FD: # LABELSST
            #print 'LABELSST',
            r, c, i = process_LABELSST(rec_data)
            values[(r, c)] = SST[i]
            #print r, c, SST[i].encode('cp866')
        elif rec_id == 0x0204: # LABEL
            #print 'LABEL',
            r, c, b = process_LABEL(biff8, rec_data)
            values[(r, c)] = b
            #print r, c, b.encode('cp866')
        elif rec_id == 0x00D6: # RSTRING
            #print 'RSTRING',
            r, c, b = process_RSTRING(biff8, rec_data)
            values[(r, c)] = b
            #print r, c, b.encode('cp866')
        elif rec_id == 0x027E: # RK
            #print 'RK',
            r, c, b = process_RK(rec_data)
            values[(r, c)] = b
            #print r, c, b
        elif rec_id == 0x00BD: # MULRK
            #print 'MULRK',
            for r, c, b in process_MULRK(rec_data):
                values[(r, c)] = b
            #print r, c, b
        elif rec_id == 0x0203: # NUMBER
            #print 'NUMBER',
            r, c, b = process_NUMBER(rec_data)
            values[(r, c)] = b
            #print r, c, b
        elif rec_id == 0x0006: # FORMULA
            #print 'FORMULA',
            r, c, x = unpack('<3H', rec_data[0:6])
            if rec_data[12] == '\xFF' and rec_data[13] == '\xFF':
                if rec_data[6] == '\x00':
                    got_str = False
                    if ord(rec_data[14]) & 8:
                        # part of shared formula
                        rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
                        stream_pos += 4                      
                        rec_data = workbook_stream[stream_pos:stream_pos+data_size]
                        stream_pos += data_size
                        if rec_id == 0x0207: # STRING
                            got_str = True
                        elif rec_id not in (0x0221, 0x04BC, 0x0236, 0x0037, 0x0036):
                            raise Exception("Expected ARRAY, SHRFMLA, TABLEOP* or STRING record")
                    if not got_str:
                        rec_id, data_size = unpack('<2H', workbook_stream[stream_pos:stream_pos+4])
                        stream_pos += 4                      
                        rec_data = workbook_stream[stream_pos:stream_pos+data_size]
                        stream_pos += data_size
                        if rec_id != 0x0207: # STRING
                            raise Exception("Expected STRING record")
                    values[(r, c)] = unpack2str(biff8, rec_data)
                elif rec_data[6] == '\x01':
                    # boolean 
                    v = ord(rec_data[8])
                    values[(r, c)] = bool(v)
                elif rec_data[6] == '\x02':
                    # error
                    v = ord(rec_data[8])
                    if v in ExcelMagic.error_msg_by_code:
                        values[(r, c)] = ExcelMagic.error_msg_by_code[v]
                    else:
                        values[(r, c)] = u'#UNKNOWN ERROR!'
                elif rec_data[6] == '\x03':
                    # empty
                    values[(r, c)] = u''
                else:
                    raise Exception("Unknown value for formula result")
            else:
                # 64-bit float
                d, = unpack("<d", rec_data[6:14])
                values[(r, c)] = d

    encoding = None
    return zip(sheet_names, sheets)

Das was mir fehlt ist eine Auswertung des XF-Records und FONT.
Eine gute Doku zum BIFF8 Format habe ich unter:

http://www.xlam.ch/soscq/dateiformat.htm
und bei oO.org
http://sc.openoffice.org/excelfileformat.pdf
gefunden.

Den Quellcode vom pyExcelerator findet man hier:
http://www.sourceforge.net/projects/pyexcelerator[/code]
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hallo,

tut mir leid: Ich habe Dich vorhin in einem Anfall geistiger Umnachtung mißverstanden :oops:. So weit ich weiß, ist es nicht möglich Formatierungen mittels pyexcelerator wiederzubekommen. Ich verwende das Modul auch, da ich hauptsächlich unter Linux arbeite aber manchmal Daten nach Excel exportieren muß - aber so weit ich sehe, bin ich der Einzige hier im Forum.

Was Du mit

Code: Alles auswählen

x = parse_xls('ein.xls')
erhälst ist eine Liste:
x[0] - der Inhalt eines Worksheets
x[0][0] - Name des Worksheets
x[0][1] - die Daten in Form eines dicts mit den Zellen als tuple von Spalte/Zeile, den den Schlüssel darstellen. Zurück bekommst Du nur den Wert (i.d.R einen Unicode-String, eine Zahl, aber noch nicht einmal Formeln :-( ).

(Das alles weißt Du wahrscheinlich längst, aber ich schreibe mal der Vollständigkeit halber, schließlich gibt es ähnliche Fragen ab und an mal.)

Die Situation ist etwas unbefriedigend, aber es gibt win32com - einfach mal hier im Forum danach suchen.

Gruß,
Christian
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

PS Ich habe vor gut einem halben Jahr einmal den Entwickler Roman Kiseliov angeschrieben. Zu diesem Zeitpunkt also konnte man ihn noch kontaktieren unter angegebenen Adresse. Und beantwortet auch alle Frage rel. zügig. Allerdings scheint er nicht an einer Weiterentwicklung des Moduls interessiert zu sein.

Gruß,
Christian
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

Hi,

danke trotzdem fürs nachschauen.

Ich habe es in die Richtung bereits hinbekommen, allerdings nicht ausgereift.

Habe in der parse_xls() noch eine Suche nach XF-Record und FONT-Record eingebaut.
Wenn ich da wieter bin melde ich mich hierzu nochmal.

Aber mal was anderes.
Hatte jetzt ein Problem mit Umlauten Äs und Üs und so, da bricht der Parser ab, weill das encode('cp688') oder wie es nochmal hieß nicht klappt.

Schonmal was in der Richtung angepasst?

Gruss
JP
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Mal interessehalber: Was ist der Unterschied zu xlrd?
BlackJack

pyExcelerator kann nur ein relativ altes Excel-Format lesen. Ich glaube bei `Excel 95` hört es auf. Dafür kann man damit auch Excel-Dateien schreiben.
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

BlackJack hat geschrieben:pyExcelerator kann nur ein relativ altes Excel-Format lesen.
Das stimmt so nicht ganz.
Ich verwende den pyExcelerator-6.0.3a.
Der Importiert auf jeden Fall Excel 2000 (getestet),
laut Quellcode wird meines erachtens immer mit BIFF8 gearbeitet, also 97 und 2000, aber auch Funktionen für Werte in älteren Versionen habe ich gesehen.

Die offizielle Beschreibung sagt:
Generating Excel 97+ files with Python 2.4+ (need decorators), importing Excel 95+ files, support for UNICODE in Excel files, using variety of formatting features and printing options, Excel files and OLE2 compound files dumper. No need in Windows/COM
Gruss
JP
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

Hallo,

nachdem ich jetzt fastalles selbst gebaut habe, was mit Formatierungen zu tun hat (XF, FONT, FORMAT), bin ich wegen Datumsdarstellungen nochmal auf xlrd gestossen.

Aber ich komm nicht so ganz zu rande mit den Klassen/Modulen.
Weiss jemand, wie ich auf die Formatierungen zugreifen kann?
Ich suche etwas um von dem XF Record Index auf das XF Record (Class) zugreifen kann, um die Werte wiederum auszulesen.

Ein paar Beispiele:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8

import xlrd

book = xlrd.open_workbook("test.xls", formatting_info=True)
print "The number of worksheets is", book.nsheets
print "Worksheet name(s):", book.sheet_names()
sh = book.sheet_by_index(2)
print sh.name, sh.nrows, sh.ncols
print "Cell A11 is", sh.cell_value(rowx=10, colx=0)
print "Cell mit Typ:Wert, (XF:Index)", sh.cell(rowx=10, colx=0)
xf = sh.cell_xf_index(rowx=10, colx=0)
print "XF", xf

dt = xlrd.xldate_as_tuple(sh.cell_value(rowx=10, colx=0),book.datemode)
print dt

import datetime
print datetime.datetime(*dt)
Ich finde entweder die Funktionen nicht, oder weiss nicht, wie ich die Listen oder Maps benutze, die mit den Formatierungen erstellt werden.

Ich möchte zum Beispiel die Schriftgröße auslesen.
Das steht in zb in der Klasse Rowinfo.
Bekomme aber nur den initial Wert:

Code: Alles auswählen

print xlrd.sheet.Rowinfo.height
Gruss
JP

PS: Zur Frage weiter odebn:
pyExcelerator ist ein umfangreiches Modul zum erstellen von Excel-Dateien
xlrd dagegen ist spezialisiert aus lesen und speichert alle Zellen und Dokumentinformationen, um sie direkt abzurufen.
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

Hallo,

ich bin schon ein bischen weitergekommen.
Ein Zugriff auf ein bestimmtes XF Element könnte so aussehen:

Code: Alles auswählen

#37 ist ein Beispiel index entweder den Wert in der Zelle (sihe oben), oder Wert -1
book.xf_list[37].dump()

Code: Alles auswählen

_alignment_flag: 0
_background_flag: 0
_border_flag: 0
_font_flag: 0
_format_flag: 1
_protection_flag: 0
alignment (XFAlignment object):
    hor_align: 0
    indent_level: 0
    rotation: 0
    shrink_to_fit: 0
    text_direction: 0
    text_wrapped: 0
    vert_align: 2
background (XFBackground object):
    background_colour_index: 65
    fill_pattern: 0
    pattern_colour_index: 64
border (XFBorder object):
    bottom_colour_index: 0
    bottom_line_style: 0
    diag_colour_index: 0
    diag_down: 0
    diag_line_style: 0
    diag_up: 0
    left_colour_index: 0
    left_line_style: 0
    right_colour_index: 0
    right_line_style: 0
    top_colour_index: 0
    top_line_style: 0
font_index: 0
format_key: 20
is_style: 0
lotus_123_prefix: 0
parent_style_index: 0
protection (XFProtection object):
    cell_locked: 1
    formula_hidden: 0
xf_index: 37
Allerdings weiss ich jetzt nicht, wie ich zB auf die Elemente des UnterObjects komme. ( background (XFBackground object): )
Jay-Pi
User
Beiträge: 19
Registriert: Dienstag 13. Februar 2007, 14:10

OK ich habe den Fehler gefunden.

Ich habe den falschen "key" benutzt. Nicht den Klassen namen benötige ich, sondern das Attribut, welches als instance die Klasse hat.

also background anstatt XFBackground

Code: Alles auswählen

book.xf_list[37].background.background_colour_index
Antworten