end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転しました

xlrd for python で excel (xlsx) を読む

またも、以前のperlでのentryのpython

Spreadsheet::ParseExcelでlinuxのperlからexcelファイルを読む - end0tknr's kipple - 新web写経開発

Mike Blackwell / Spreadsheet-XLSX - search.cpan.org

Douglas Wilson / Spreadsheet-ParseExcel - search.cpan.org

python用 xlsx module は、openpyxl と xlrd の2種類

www.python-excel.org には、 xlrd は xls用と記載されていますが、xlrdも xlsxを読めるようです。

ただ、後述するように数式による値を算出できない場合もあるようですので openpyxl がよいと思います。

http://www.python-excel.org/

openpyxl - A Python library to read/write Excel 2010 xlsx/xlsm files — openpyxl 2.4.9 documentation

xlrd documentation — xlrd 1.1.0 documentation

xlrd/openpyxl for python と Spreadsheet::XLSX/Spreadsheet::ParseExcel for perl の相違点

今回、気づいた範囲では、 数式のあるcellの値を参照した際の返り値が異なっていました。

lang module method 値or数式
perl Spreadsheet::XLSX $cell->value()
perl Spreadsheet::ParseExcel $cell->value() 値 ※1
python xlrd cell.value 値 ※2
python openpyxl cell.value 数式or値 ※3

※1 2017/9/3時点のSpreadsheet::ParseExcelのdocumentを読むと TODOに「Add Formula support」と記載されていました。

※2 後日、xlrd を試していたら、SUM()の値を取得できず、 0となることがありました。

※3 load_workbook(xlsx_path, data_only=True) で開くと、数式でなく値を取得できます

xlrd for python の sample script

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import getopt
import sys
import xlrd

def main():
    xlsx_path = sys.argv[1]
    wbook = xlrd.open_workbook(xlsx_path)
    
    for sheet_name in wbook.sheet_names():
        print '## SHEET_NAME=' + sheet_name
        wsheet = wbook.sheet_by_name(sheet_name)

        for row in range(wsheet.nrows):
            for col in range(wsheet.ncols):
                cell = wsheet.cell(row,col)
                val = cell.value

                if val == None:
                    continue

                print "(%d,%d)=%s" % (row,col,val)

if __name__ == '__main__':
    main()

↑こう書くと↓こう表示されます

## SHEET_NAME=Sheet1
(0,0)=1.0
(0,1)=2.0
(0,2)=3.0
(1,0)=4.0
(1,1)=5.0
(1,2)=6.0
(2,0)=5.0
(2,1)=7.0
(2,2)=9.0

openpyxl for python の sample script

数式がそのまま表示されることが分かります

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
from openpyxl import load_workbook
import getopt
import sys


def main():
    xlsx_path = sys.argv[1]
    wbook = load_workbook(xlsx_path)
    
    for sheet_name in wbook.get_sheet_names():
        print '## SHEET_NAME=' + sheet_name
        wsheet = wbook.get_sheet_by_name(sheet_name)

        row = wsheet.min_row
        row_max = wsheet.max_row
        col_min = wsheet.min_column
        col_max = wsheet.max_column

        while row <= row_max:
            col = col_min
            while col <= col_max:
                cell = wsheet.cell(row=row,column=col)
                
                val = cell.value
                if val == None:
                    col += 1
                    continue
                val_2 = cell.internal_value
                print "(%d,%d)=%s,%s" % (row,col,val,val_2)
                col += 1
                
            row += 1

if __name__ == '__main__':
    main()

↑こう書くと↓こう表示されます

## SHEET_NAME=Sheet1
(1,1)=1,1
(1,2)=2,2
(1,3)=3,3
(2,1)=4,4
(2,2)=5,5
(2,3)=6,6
(3,1)==SUM(A1:A2),=SUM(A1:A2)
(3,2)==SUM(B1:B2),=SUM(B1:B2)
(3,3)==SUM(C1:C2),=SUM(C1:C2)

Spreadsheet::XLSX for perl の sample script

#!/usr/local/bin/perl
use strict;
use warnings;
use Encode;
use Spreadsheet::XLSX;
use Data::Dumper;

main(@ARGV);

sub main {
    my ($xlsx_path) = @_;

    my $wbook = Spreadsheet::XLSX->new($xlsx_path);
    for my $wsheet (@{$wbook -> {Worksheet}}) {
        print "## SHEET_NAME= $wsheet->{Name}\n";
        
        my $row =       $wsheet->{MinRow};
        my $row_max =   $wsheet->{MaxRow};
        my $col_min =   $wsheet->{MinCol};
        my $col_max =   $wsheet->{MaxCol};
        print "RANGE=$row,$row_max,$col_min,$col_max\n";

        while($row<=$row_max) {
            my $col = $col_min;
            while($col<=$col_max) {
                my $cell = $wsheet->{Cells}[$row][$col];
                if(not $cell){
                    $col += 1;
                    next;
                }

                my $val = $cell->value();
                my $val_2 = $cell->unformatted();
                print "($row,$col)=$val , $val_2\n";
                $col += 1;
            }
            $row += 1;
        }

    }
}

↑こう書くと↓こう表示されます

## SHEET_NAME= Sheet1
RANGE=0,2,0,2
(0,0)=1 , 1
(0,1)=2 , 2
(0,2)=3 , 3
(1,0)=4 , 4
(1,1)=5 , 5
(1,2)=6 , 6
(2,0)=5 , 5
(2,1)=7 , 7
(2,2)=9 , 9