Excelファイルから文字列を取り出す

Python

Excelから文字列だけを取り出したい!という需要はあまりないと思いますが、画像を取り出すツール(こちら)がありますので、どうせならと作ってみました。

原理は画像取り出しツールと基本同じですが、文字列はXMLファイルに埋め込まれており単純にファイルをごそっと抜き取るだけでは使い物になりません。XMLパーサーで必要な部分だけを抜き出して、それをファイルに書き出すという処理が必要になります。

なお、取り出せるのは文字列だけで計算式の結果は対象外です。よって対神エクセル専用ツールとお考えください。

スクリプトはPythonでソースコードは次の通りです。

import xml.etree.ElementTree as et
import sys
import os
import zipfile

try:
    excel_file = sys.argv[1]

    p = sys.executable if getattr(sys, 'frozen', False) else __file__
    cd = os.path.dirname(os.path.abspath(p))

    with zipfile.ZipFile(excel_file) as xlsx:
        ns = {
            'xlsx': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
            'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'
        }
        str_root = et.fromstring(xlsx.read('xl/sharedStrings.xml'))
        values = str_root.findall('xlsx:si/xlsx:t', ns) + \
                 str_root.findall('xlsx:si/xlsx:r/xlsx:t', ns)
        os.makedirs(os.path.join(cd, 'xl'), exist_ok=True)

        for draw in xlsx.namelist():
            if 'xl/drawings' in draw:
                draw_root = et.fromstring(xlsx.read(draw))
                values += draw_root.findall('.//a:t', ns)

        with open(os.path.join(cd, 'xl', 'strings.txt'), 'w') as result:
            for value in values:
                try:
                    result.write(value.text + '\n')
                except UnicodeEncodeError:
                    pass
except:
    pass

解説

神エクセル撲滅協会理事(自称)である私のPC上には神エクセルの存在が許されていません。そこでサンプルとなるファイルを我らがさいたま市のWEBサイトで探してみたら、やっぱりありました。

別にさいたま市がダメなのではありません。仕事がら経済産業省とかの書式を使いますが、これと大差ないです。これが国家の中枢の仕事なのかと愕然となります。もはや国家ぐるみです。まぁ、使えるものをどう使おうが自由だと言われたら何も反論できないんですけどね。

サンプルデータはこちらのページの「省エネ対策詳細表」です。

列表示を見るだけで、ほとばしる神感を感じていただけるかと思います。

さて、画像ツールのページには書いてありますが、Excelファイルの実体はXMLと付帯データをZIP圧縮したものです。Excelシートの文字データはその中の【xl/sharedStrings.xml】に記述されています。ここに

<si><t>文字列</t></si>
または
<si><r><t>文字列</t></r></si>

という構造で文字列が格納されています。二つの違いはよくわかりません。rタグの中身を見た感じ、フォントを変更するなど、デフォルトと違うスタイルを文字に指定すると<r>配下になると思われます。

ExcelのXMLについては詳しい仕様書が公開されているのですが、5000ページ以上あるPDFで全部英語で読む気力も能力もありません。

サンプルファイルのXMLはこんな感じです。

よって不確定要素が多く、あまりスマートではないですが、パーサーでこの2パターンを引っ張ってきます。XMLパーサーはPython標準のElementTreeを使用します。

nsはネームスペースとPrefixとの対応を定義した辞書で、パーサーに渡しておく必要があります。

ns = {
    'xlsx': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
    'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'
}
str_root = et.fromstring(xlsx.read('xl/sharedStrings.xml'))
values = str_root.findall('xlsx:si/xlsx:t', ns) + str_root.findall('xlsx:si/xlsx:r/xlsx:t', ns)

もう一つExcelシートに文字を表示する手法として、テキストボックスがあります。テキストボックスの文字列情報はsharedStrings.xmlには記述されません。

では、どこに記述されているかというと、サンプルファイルの場合【xl/drowings/】配下に【drawing1.xml】という名前でありました。このdrawing1.xmlというファイルがテキストボックスを増やすと2、3と増えていきそうな気がしたので、試しに1つテキストボックスを追加してみたのですが、drawing1.xmlにその情報が追記されただけでした。

しかし思わせぶりなファイル名が気になるので、どのタイミングで増えても、何個存在してもいいように、forループで総当たりでパースすることにします。

テキストボックスの文字データはXMLに
<a:t>文字列</a:t>
のように格納されています。

for draw in xlsx.namelist():
    if 'xl/drawings' in draw:
        draw_root = et.fromstring(xlsx.read(draw))
        values += draw_root.findall('.//a:t', ns)

最後にパーサーが収集した文字列をまとめてテキストファイルへ書き出します。画像ツールにならって【xl/strings.txt】という構成で出力します。エラーハンドラはPythonが処理できない特殊文字があった場合に処理が中断されてしまうのを防いでいます。処理できない文字を含むデータは書き出されません。

使用方法

拡張子xlsのファイルは処理できません。xlsxに保存しなおすか、あきらめてください。

1.このページのダウンロードボタンからアプリを入手して適当なフォルダへ配置してください。どこの馬の骨ともわからないサイトの実行ファイルなんて使えねーというかたはソースコードをコピペしてお使いください。

2.アプリへ対象のExcelファイルをドラッグ&ドロップします。コマンドプロンプトの黒い画面が一瞬見えると思いますが、こいつの仕業です。ウイルスではありません。ご安心ください。

Excelファイルがあるフォルダへ【xl】というフォルダができます。

【xl】フォルダの中の strings.txtファイルに文字データあります。Excelでの見た目通りの順序で記録されません(元になるXMLがおそらく入力した順に記録されているためです)。

サンプルファイルの実行結果は正確に検証していませんが、テキストデータの8割くらいは抽出されている感じです。しかし今回のサンプルで一番重要と思われる省エネ対策に対する助成金一覧表がまさかの画像データだったので取れませんでした。全体の情報量からすると半分程度になってますね。

神エクセル侮りがたし。

本気でやるなら手軽さはガクンと落ちますが、素直にExcel-VBAでセルデータを書き出す処理を作った方がいい気がします。気が向いたらやってみたいと思います。

アプリを入手する

アプリで使用しているモジュールElementTreeは悪意を持って作成されたデータに対して安全ではありません。信頼できないデータをパースする場合はご注意ください。詳しくはPythonリファレンスページをご確認ください。

利用上のご注意

  • ダウンロードしたファイルを利用したことにより生じた結果については、利用者ご自身に責任を負っていただきます。
  • ご利用前に使用方法をご確認ください。
  • 当方は成果物の正確性について最善を尽くしますが保証はいたしません。
  • Windows11 Microsoft365 環境でのみ動作確認済み。

Downloadボタンを押下した時点で注意事項に同意したものとみなします。

excel_string_extraction.exe

おわり。