web-dev-qa-db-fra.com

La lecture du fichier Excel est plus lente avec openpyxl que xlrd

J'ai une feuille de calcul Excel que je dois importer quotidiennement dans SQL Server. La feuille de calcul contiendra environ 250 000 lignes sur environ 50 colonnes. J'ai testé à la fois openpyxl et xlrd en utilisant un code presque identique.

Voici le code que j'utilise (moins les instructions de débogage):

import xlrd
import openpyxl

def UseXlrd(file_name):
    workbook = xlrd.open_workbook(file_name, on_demand=True)
    worksheet = workbook.sheet_by_index(0)
    first_row = []
    for col in range(worksheet.ncols):
        first_row.append(worksheet.cell_value(0,col))
    data = []
    for row in range(1, worksheet.nrows):
        record = {}
        for col in range(worksheet.ncols):
            if isinstance(worksheet.cell_value(row,col), str):
                record[first_row[col]] = worksheet.cell_value(row,col).strip()
            else:
                record[first_row[col]] = worksheet.cell_value(row,col)
        data.append(record)
    return data


def UseOpenpyxl(file_name):
    wb = openpyxl.load_workbook(file_name, read_only=True)
    sheet = wb.active
    first_row = []
    for col in range(1,sheet.max_column+1):
        first_row.append(sheet.cell(row=1,column=col).value)
    data = []
    for r in range(2,sheet.max_row+1):
        record = {}
        for col in range(sheet.max_column):
            if isinstance(sheet.cell(row=r,column=col+1).value, str):
                record[first_row[col]] = sheet.cell(row=r,column=col+1).value.strip()
            else:
                record[first_row[col]] = sheet.cell(row=r,column=col+1).value
        data.append(record)
    return data

xlrd_results = UseXlrd('foo.xls')
openpyxl_resuts = UseOpenpyxl('foo.xls')

Passer le même fichier Excel contenant 3500 lignes donne des temps d'exécution radicalement différents. En utilisant xlrd je peux lire le fichier entier dans une liste de dictionnaires en moins de 2 secondes. En utilisant openpyxl j'obtiens les résultats suivants:

Reading Excel File...
Read 100 lines in 114.14509415626526 seconds
Read 200 lines in 471.43183994293213 seconds
Read 300 lines in 982.5288782119751 seconds
Read 400 lines in 1729.3348784446716 seconds
Read 500 lines in 2774.886833190918 seconds
Read 600 lines in 4384.074863195419 seconds
Read 700 lines in 6396.7723388671875 seconds
Read 800 lines in 7998.775000572205 seconds
Read 900 lines in 11018.460735321045 seconds

Bien que je puisse utiliser xlrd dans le script final, je devrai coder en dur beaucoup de mise en forme en raison de divers problèmes (par exemple, int lit comme float, date lit comme int, datetime lit comme float). Étant donné que j'ai besoin de réutiliser ce code pour quelques importations supplémentaires, il n'est pas logique d'essayer de coder en dur des colonnes spécifiques pour les formater correctement et de conserver un code similaire sur 4 scripts différents.

Des conseils sur la façon de procéder?

15
Ron Johnson

Vous pouvez simplement itérer sur la feuille:

def UseOpenpyxl(file_name):
    wb = openpyxl.load_workbook(file_name, read_only=True)
    sheet = wb.active
    rows = sheet.rows
    first_row = [cell.value for cell in next(rows)]
    data = []
    for row in rows:
        record = {}
        for key, cell in Zip(first_row, row):
            if cell.data_type == 's':
                record[key] = cell.value.strip()
            else:
                record[key] = cell.value
        data.append(record)
    return data

Cela devrait évoluer vers des fichiers volumineux. Vous voudrez peut-être fragmenter votre résultat si la liste data devient trop grande.

Maintenant, la version openpyxl prend environ deux fois plus de temps que la xlrd:

%timeit xlrd_results = UseXlrd('foo.xlsx')
1 loops, best of 3: 3.38 s per loop

%timeit openpyxl_results = UseOpenpyxl('foo.xlsx')
1 loops, best of 3: 6.87 s per loop

Notez que xlrd et openpyxl peuvent interpréter ce qui est un entier et ce qui est un flottant légèrement différemment. Pour mes données de test, j'ai dû ajouter float() pour rendre les sorties comparables:

def UseOpenpyxl(file_name):
    wb = openpyxl.load_workbook(file_name, read_only=True)
    sheet = wb.active
    rows = sheet.rows
    first_row = [float(cell.value) for cell in next(rows)]
    data = []
    for row in rows:
        record = {}
        for key, cell in Zip(first_row, row):
            if cell.data_type == 's':
                record[key] = cell.value.strip()
            else:
                record[key] = float(cell.value)
        data.append(record)
    return data

Maintenant, les deux versions donnent les mêmes résultats pour mes données de test:

>>> xlrd_results == openpyxl_results
True
14
Mike Müller

Cela me semble être un candidat parfait pour le module Pandas :

import pandas as pd
import sqlalchemy
import pyodbc

# pyodbc
#
# assuming the following:
# username: scott
# password: tiger
# DSN: mydsn
engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')

# pymssql
#
#engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')


df = pd.read_Excel('foo.xls')

# write the DataFrame to a table in the sql database
df.to_sql("table_name", engine)

Description de la fonction DataFrame.to_sql ()

PS Il devrait être assez rapide et très facile à utiliser

1
MaxU