プログラムでおかえしできるかな

定年を過ぎて何かの役に立てないかなと始めた元SEのブログです

エクセルの表からMarkdownの表に変換【Python】

このエントリーをはてなブックマークに追加

ブログの記述などに使われるMarkdown記法の中で、表の記述は少し面倒です。
そこで、エクセルで表を作成し、その表をMarkdown記法に変換するツールをPythonの勉強を兼ねて作成しました。
エクセルファイルを読み、Markdown記法に変換してテキストファイルを出力します。
Pythonのパッケージとして openpyxl を使う場合と、 pandas を使う場合について紹介します。

目次

◆できること

エクセルの表をMarkdown記法で記述したテキストファイルに出力します。

エクセル⇒ Markdownテキスト⇒ ブラウザ表示
エクセル Markdown ブラウザ表示

◆考え方

エクセルのファイルを変換してcsvを出力します。
この時に、区切り文字を , から | に変換して出力します。

更に、次の考慮をします。

  • エクセルの1行目は見出しとする。ただし、処理ではデータとして扱う。
  • セル内で改行している場合 <br> タグに置き換えて対応
  • Matkdownに必要な、見出しと内容の区切り(アラインメント行)を追加

Markdown記法の表

Markdown記法の表は、カラムの区切りを |(縦棒)にし、見出しと内容の間に、-(ハイフン)で区切りの行(アラインメント行)を指定します。

Markdown記法の表のサンプル
Head1 | Head2 | Head3
---   | ---   | ---  
Data1 | Data2 | Data3
Data4 | Data5 | Data6

◆処理内容

Pythonにはエクセルを扱うパッケージがいくつか提供されています。
今回は、openpyxlとpandasを使用した場合について説明します。
pandasの方が短いステップ数で実装できますが、パッケージは大きいです。

◇openpyxlの場合

  1. エクセルファイルをロードする。
    読み取り専用 read_only=True 、値として読む data_only=True
    _wb = oxl.load_workbook( filename=input_file, read_only=True, data_only=True)
    • 扱うシートを先頭のシートにする。
      _sheet = _wb.worksheets[0]
  2. 改行されているセルの改行文字を <br> に置換
    シートのセルを内包表記(行、列で)で置換したセルに代える
    _rows = [[str(x).replace("\n", "<br>") for x in _rows] for _rows in _sheet.values]
  3. Markdownの表のアラインメント行データを作成(値を「---」として行追加)
    列数分のリストを内包表記で作成
    kugiri = ["---" for _ in range(_sheet.max_column)]
  4. 元のリストを1行目とそれ以外に分割して間にアラインメント行データを挿入
    dfs = _rows[:1] + [kugiri] +_rows[1:]
  5. csvファイルに出力 ( Write_csv(output_path, [], dfs) メソッド)
    1. ファイルを開く
      with open(file_name, encoding="utf_8_sig", mode="w", newline="\n") as f:
    2. 区切り文字「 | 」を指定
      _writer = csv.writer(f, delimiter="|")
    3. すべてのデータを出力
      _writer.writerows(rows)

◇pandasの場合

  1. エクセルファイルをリードする。
    ヘッダーなしとする header=None
    dfs = pd.read_excel(input_file, header=None)
    ※xlsxファイルをpandasで読むにはopenpyxlが必要(importは不要、インストールは必要)
  2. 改行されているセルの改行文字を <br> に置換
    ヘッダーなしで読むことで1行目のデータも置換対象とする
    dfs.replace("\n", "<br>", True, regex=True)
    ※DataFrameのreplace()メソッドは、各要素を置換する
    ※replace()メソッドで文字列の一部を置換する場合は、正規表現( regex=True )で行う
  3. Markdownの表のアラインメント行データを作成(値を「---」として行追加)
    列数分の辞書を内包表記で作成
    kugiri = {col:"---" for col in dfs.columns}
  4. 元のリストを1行目とそれ以外に分割して間にアラインメント行データを挿入
    dfs = dfs.iloc[:1].append( kugiri, ignore_index=True).append( dfs.iloc[1:])
  5. csvファイルに出力
    区切り文字( sep="|" )、行名なし( index=False )、見出しなし( header=False )を指定。
    dfs.to_csv(output_path, sep="|", index=False, header=False)



◆全体のソース

処理内容では触れていませんが、エクセルファイルの指定をコマンドライン引数から行うようにしています。
また、出力ファイル名は「月日_時分_秒.txt」です。

◇openpyx

import openpyxl as oxl
import sys, datetime, csv

def Write_csv( file_name:str, headers:list, rows:list):
    """
    CSVファイルの出力   utf_8_sig, crlf

    Args:
        str:        ファイル名
        list:       ヘッダ用リスト
        list:       出力データ用リスト
    """
    try:
        with open(file_name, encoding="utf_8_sig", mode="w", newline="\n") as f:
            _writer = csv.writer(f, delimiter="|")
            for header in headers:
                _writer.writerow(header)
            _writer.writerows(rows)
    except Exception as e:
        print("CSVエラー", e)


# コマンドライン引数からエクセルファイル名を取得
if len(sys.argv) < 2:
    print("error no input file name")
    sys.exit(1)
input_file = sys.argv[1]

print("Convert file:{}".format(input_file))

_wb = oxl.load_workbook(filename=input_file, read_only=True, data_only=True)    # エクセルファイルの読み込み
_sheet = _wb.worksheets[0]            # 先頭のシートの取得
# 各セルの改行文字を置換して戻す
_rows = [[str(x).replace("\n", "<br>") for x in _rows] for _rows in _sheet.values]
kugiri = ["---" for _ in range(_sheet.max_column)]  # markdown表のアラインメント行の作成
dfs = _rows[:1] + [kugiri] +_rows[1:]               # アラインメント行の追加
_wb.close()

output_path = '{}.txt'.format(datetime.datetime.now().strftime("%m%d_%H%M_%S"))
Write_csv(output_path, [], dfs)                     # csvファイルへ出力
print("Convert end")

◇pandas

import pandas as pd
import sys, datetime

# コマンドライン引数からエクセルファイル名を取得
if len(sys.argv) < 2:
    print("error no input file name")
    sys.exit(1)
input_file = sys.argv[1]

print("Convert file:{}".format(input_file))

dfs = pd.read_excel(input_file, header=None)    # エクセルファイルの読み込み
dfs.replace("\n", "<br>", True, regex=True)     # 各セルの改行文字を置換
kugiri = {col:"---" for col in dfs.columns}     # markdown表のアラインメント行の作成
dfs = dfs.iloc[:1].append(kugiri, ignore_index=True).append(dfs.iloc[1:])   # アラインメント行の追加

output_path = '{}.txt'.format( datetime.datetime.now().strftime( "%m%d_%H%M_%S"))
dfs.to_csv(output_path                          # csvファイルへ出力
                , sep="|"                       # 区切り文字
                , index=False                   # 行名を出力しない
                , header=False                  # 見出しを出力しない
                )
print("Convert end")

◇全体のソースの取得先

全体のソースはこちらからも取得できます。
リンク:GitHub juu7g/Python-excel2MD

◆exeファイル

pyinstallerexe ファイルを作成しました。

  • pyinstaller -F pyファイル名

簡単なプログラムですが、パッケージの大きさで exe ファイルのサイズがかなり違います。
ちなみに、 pyinstaller は最小限のパッケージのみをいれた仮想環境で動かしています。

  • openpyxl 使用:約8MB
  • pandas 使用:約30MB

◇バイナリ取得先

バイナリも公開します。サイズの小さいopenpyxlパッケージを使用したものです。
こちらから取得してください。ダウンロード

◇バイナリの使い方

使い方: excel2md_oxl.exe input_path

位置引数:
  input_path        入力ファイル名

ダウンロードしたzipファイルを解凍してできたexeファイルを実行します。
出力ファイル名は「月日_時分_秒.txt」です。

◆さいごに

エクセルの表を読んでMarkdown記法の表データに変換してテキストファイルに出力するPythonプログラムを紹介しました。

短いプログラムなので『考え方』を読んで、実際に作ってみて、うまくいかなかったら『処理内容』を読んでみるという読み方もあるかなと思います。

今回は、先頭のシートしか処理していないので、シートの指定を可能にするなどの拡張をしても良いのかなと思っています。

これで少し量の多い表でもMarkdownの表にするのが楽になると思います。

あわせて読みたい - openpyxl関連記事 Excel viewerアプリの作り方(Tkinter-Notebook-Treeview)【Python】
追加:2021-10-04

◇ご注意

本記事は次のバージョンの下で動作した内容を基に記述しています。

  • Python 3.8.5
  • openpyxl 3.0.5
  • pandas 1.2.2

ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。
追加:2022-08-08

■更新情報

◆参考

投稿: 、更新: