Node.jsでExcelを読み込むライブラリ「xlsx(旧:SheetJS)」を使ってみた。

皆さん、こんにちは。技術開発グループのn-ozawanです。
先日スーパーで、キャベツ1玉500円もしてました。高くてなかなか手が出ません。キャベツは炒め物はもちろんのこと、ごま油と塩でナムル風にしてもおいしいです。

本題です。
表形式の文書を記述するのにExcelは大変便利です。仕様書や設計書などはMarkdownやWikiなどで記述しますが、WBSやテスト仕様書など、表形式で表現するものはExcelで記述しています。今回はそんなExcelをNode.jsで読み込むライブラリ「xlsx」を使ってみましたので、紹介します。

xlsx(旧:SheetJS)

概要

xlsxはExcelへの読み書きが出来るNode.jsのライブラリです。Excel関連のライブラリは数多く存在しますが、その中で一番ダウンロードされており、多くの人から支持されています。以前はSheetJSという名前だったのですが、xlsxに改名されました。

xlsxは有料版と無料版があります。無料版ではExcelのスタイル設定や、画像挿入など、多くのことが出来ません。とはいえ、単にExcelの内容を読み取るだけであれば無料版でも十分です。

インストール

xlsxのインストールは以下の通りです。当然ですが、Node.jsとnpmの環境が必要です。

npm i xlsx

Excelを読み込む

では早速Excelファイルを読み込むコードを実装してみましょう。Excelは事前に以下を用意しておきます。

ソースコードは以下の通りです。TypeScriptで記述しています。

import * as xlsx from "xlsx"
import { WorkBook, WorkSheet } from "xlsx"

const workbook: WorkBook = xlsx.readFile("example.xlsx");
const sheet: WorkSheet = workbook.Sheets["Example01"];
const rows: any[] = xlsx.utils.sheet_to_json(sheet);
console.log(rows);

4行目のxlsx.readFile(...)は、Excelファイルを読み込み、WorkBookを返却します。

取得したWorkBookSheetsからWorkSheetを取得することが出来ます。WorkSheetはその名の通り、シート情報になります。取得する際はシート名を指定します。

取得したWorkSheetを使って、xlsx.utils.sheet_to_json(...)関数により行配列を取得します。取得した行配列をconsole.log(...)で表示すると以下のようになります。

[
  {
    'No.': 1,
    'テスト内容': '自分の足で立って歩けること',
    '実施日': 45597,
    '実施者': 'n-ozawan',
    '実施結果': 'OK'
  },
  {
    'No.': 2,
    'テスト内容': '自分の足で立って走れること',
    '実施日': 45598,
    '実施者': 'n-ozawan',
    '実施結果': 'NG',
    '備考': '歳には勝てない。'
  }
]

値を取得したい場合は以下のようにします。

console.log(rows[1]["テスト内容"]);   // 自分の足で立って走れること

日付を取得する方法

先ほどのソースコードでは、実施日がExcelのシリアル値になっています。xlsxは、デフォルトではシリアル値を返却します。日付を正しく取得するには以下のようにします。

const workbook: WorkBook = xlsx.readFile("example.xlsx", { cellText: false, cellDates: true });
const sheet: WorkSheet = workbook.Sheets["Example01"];
const rows: any[] = xlsx.utils.sheet_to_json(sheet, { raw: false, dateNF: 'yyyy"-"mm"-"dd' });
console.log(rows);

xlsx.readFile(...){ cellText: false, cellDates: true }を指定することで、日付のセルを正しく読み込めるようにします。また、xlsx.utils.sheet_to_json(...)の第2引数を{ raw: false, dateNF: 'yyyy"-"mm"-"dd' }とすることで、日付のフォーマットを指定することが出来ます。実行すると以下のようになります。

[
  {
    'No.': '1',
    'テスト内容': '自分の足で立って歩けること',
    '実施日': '2024-11-01',
    '実施者': 'n-ozawan',
    '実施結果': 'OK'
  },
  {
    'No.': '2',
    'テスト内容': '自分の足で立って走れること',
    '実施日': '2024-11-02',
    '実施者': 'n-ozawan',
    '実施結果': 'NG',
    '備考': '歳には勝てない。'
  }
]

変則的な表の場合

これまでのコードは、rows[1]["テスト内容"]のように、ヘッダーを自動識別して値を設定してくれています。これは、xlsxが1行目をヘッダーとして処理してくれているためです。例えば以下の表があったとします。

この場合、以下のようになります。

[
  { '総数': '2', 'OK件数': '1' },
  {
    __EMPTY: 'No.',
    __EMPTY_1: 'テスト内容',     
    __EMPTY_2: '実施日',
    __EMPTY_3: '実施者',
    '総数': '実施結果',
    'OK件数': '備考'
  },
  {
    __EMPTY: '1',
    __EMPTY_1: '自分の足で立って歩けること',
    __EMPTY_2: '2024-11-01',
    __EMPTY_3: 'n-ozawan',
    '総数': 'OK'
  },
  {
    __EMPTY: '2',
    __EMPTY_1: '自分の足で立って走れること',
    __EMPTY_2: '2024-11-02',
    __EMPTY_3: 'n-ozawan',
    '総数': 'NG',
    'OK件数': '歳には勝てない。'
  }
]

xlsxは1行目にある「総数」と「OK件数」をヘッダー項目と認識します。しかし、A列などは空欄となっていますので、実際に取得するとkeyが__EMPTYとなってしまいます。

この問題を解決する方法として、xlsx.utils.sheet_to_json(...)を呼ぶ際に、headerAを指定することで、アルファベットの列名が扱われるようになります。

const rows: any[] = xlsx.utils.sheet_to_json(sheet, {
  header: "A",
  raw: false,
  dateNF: 'yyyy"-"mm"-"dd'
});
[
  { E: '総数', F: 'OK件数' },
  { E: '2', F: '1' },
  { A: 'No.', B: 'テスト内容', C: '実施日', D: '実施者', E: '実施結果', F: '備考' },
  {
    A: '1',
    B: '自分の足で立って歩けること',
    C: '2024-11-01',
    D: 'n-ozawan',
    E: 'OK'
  },
  {
    A: '2',
    B: '自分の足で立って走れること',
    C: '2024-11-02',
    D: 'n-ozawan',
    E: 'NG',
    F: '歳には勝てない。'
  }
]

アルファベットじゃ使いにくい!という方は、ヘッダー項目を直接指定することも出来ます。

const rows: any[] = xlsx.utils.sheet_to_json(sheet, {
  header: [ "No.", "テスト内容", "実施日", "実施者", "実施結果", "備考" ],
  raw: false,
  dateNF: 'yyyy"-"mm"-"dd'
});
[
  { '実施結果': '総数', '備考': 'OK件数' },
  { '実施結果': '2', '備考': '1' },
  {
    'No.': 'No.',
    'テスト内容': 'テスト内容',
    '実施日': '実施日',
    '実施者': '実施者',
    '実施結果': '実施結果',
    '備考': '備考'
  },
  {
    'No.': '1',
    'テスト内容': '自分の足で立って歩けること',
    '実施日': '2024-11-01',
    '実施者': 'n-ozawan',
    '実施結果': 'OK'
  },
  {
    'No.': '2',
    'テスト内容': '自分の足で立って走れること',
    '実施日': '2024-11-02',
    '実施者': 'n-ozawan',
    '実施結果': 'NG',
    '備考': '歳には勝てない。'
  }
]

余計な行はslice(...)などで適宜削除してください。

おわりに

無料版のxlsxはスタイルや画像が扱えませんので、Excelファイルの書き出しには不向きかと思います。しかし、単純にExcelの中身を見たいのであれば十分ではないでしょうか。

なお、Excel関連のライブラリで、xlsx以外にはExcelJSというライブラリもあります。xlsxに次いで2番目に人気のライブラリです。こちらはxlsxと違って高機能で色々出来そうですが、Issueの数が多いのが気になります。機会があればExcelJSも使ってみたいと思います。

ではまた。

Recommendおすすめブログ