akuruhinode's blog

pythonやC#を中心に興味を持った内容について調べています。

Python Word文書の見出しをテキストファイルに変換

はじめに

Wordで作成した文書をテキストファイルに変換するスクリプトを紹介します。python-docxを利用するので、インストールされていない場合は以下のコマンドでインストールしてください。

pip install python-docx

バージョン情報

python: 3.7.9
python-docx: 0.8.10

ソースコード

ソースコードと使用例、引数と補足説明は以下の通りです。

注意点
python-docxでは見出し番号がアウトラインとして自動設定されている場合、その番号を取得することができません。そのため、単純に変換すると見出しのレベルがわからなくなってしまいます。

見出しのレベルと番号を取得するには、そのための処理を作成する必要があります。ここでは以下の2つのパターンのソースコードを紹介します。

なお、見出し番号が自動設定ではない場合(番号がすべて直入力されている場合など)は、このような処理は不要です。

見出しレベルをスペースに変換する

関数
import docx, re

# Word文書のタイトル、見出しをテキストファイルに変換
def doc2txt_only_header(docfile, txtfile, title=True):

    full_text = []
    # wordファイルの読み込み
    doc = docx.Document(docfile)
    # 各段落を取得
    for para in doc.paragraphs:

        # paragraphオブジェクトの.style.nameからスタイルを判定        
        if title & para.style.name.startswith('Title'):
            full_text.append(para.text)
                
        if para.style.name.startswith('Heading'):
            full_text.append(convert_index_to_space(
                para.style.name) + para.text)

    # 改行文字でリストを連結して文字列に変換
    full_text = '\n'.join(full_text)
    print(full_text)

    # テキストファイルとして保存
    file_for_save = open(txtfile, 'w', encoding="utf-8")
    file_for_save.write(full_text)
    file_for_save.close()

# 見出しレベルをスペースに変換する
def convert_index_to_space(style_name):
    index_regex = re.compile('Heading (\d+)')
    result = index_regex.match(style_name)
    return ' ' * int(result.group(1))

使用例
doc2txt_only_header('sample.docx', 'sample.txt')
引数
  • docxfile:Wordファイル名(入力)
  • txtfile:テキストファイル名(出力)
  • title:表題を出力するかどうか(True/False)
出力例

サンプルのWord文書と実際の出力例は以下の通りです。少しややこしいですが、見出し番号はアウトラインで設定しており、「見出し ○○」の○○の番号は確認用としてすべて手動で入力した文字列になります。
f:id:akuruhinode:20210516174124j:plain

出力されるテキストファイルの内容は以下の通りです。

表題
 見出し1        
  見出し1.1      
   見出し1.1.1   
    見出し1.1.1.1
    見出し1.1.1.2
  見出し1.2      
 見出し2
  見出し2.1      
   見出し2.1.1   
   見出し2.1.2   

見出しレベルを通し番号に変換する

関数
import docx, re

# Word文書のタイトル、見出しをテキストファイルに変換
def doc2txt_only_header(docfile, txtfile, title=True):

    full_text = []
    # wordファイルの読み込み
    doc = docx.Document(docfile)
    # 各段落を取得
    for para in doc.paragraphs:

        # paragraphオブジェクトの.style.nameからスタイルを判定        
        if title & para.style.name.startswith('Title'):
            full_text.append(para.text)
                
        if para.style.name.startswith('Heading'):
            full_text.append(convert_index_to_number(
                para.style.name) + para.text)

    # 改行文字でリストを連結して文字列に変換
    full_text = '\n'.join(full_text)
    print(full_text)

    # テキストファイルとして保存
    file_for_save = open(txtfile, 'w', encoding="utf-8")
    file_for_save.write(full_text)
    file_for_save.close()


header_index = {}

# 見出しレベルから通し番号を作成する
def convert_index_to_number(style_name, sep='.'):
    index_regex = re.compile('Heading (\d+)')
    result = index_regex.match(style_name)
    index = int(result.group(1))

    # 各見出しレベルの番号を更新
    if index in header_index.keys():
        header_index[index] += 1

        # 上位の見出しレベルが変わったら下位の値をリセットする
        for i in range(index + 1, len(header_index) + 1):
            header_index[i] = 0
    else:
        header_index[index] = 1

    # 見出しの通し番号を作成
    full_index = ''
    for i in range(1, index + 1):
        full_index += str(header_index[i]) + sep

    # 最後のsep文字をスペースに変換
    return full_index[:-1] + ' '
出力例

見出しの番号が正しく反映されていることがわかると思います。

表題
1 見出し1
1.1 見出し1.1
1.1.1 見出し1.1.1
1.1.1.1 見出し1.1.1.1
1.1.1.2 見出し1.1.1.2
1.2 見出し1.2
2 見出し2
2.1 見出し2.1
2.1.1 見出し2.1.1
2.1.2 見出し2.1.2

使用方法、引数は見出しレベルをスペースに変換すると同じなので省略します。

補足

関数内の主要な処理はコメントの通りです。
paragraph.style.nameを確認することで、表題、見出し、本文などの種別を判別できます。
例えば、以下のようになっています。

paragraph.style.name 説明
Title 表題
Heading X 見出し(Xに見出しレベルの整数が入ります)
Normal 本文

詳しくは以下の公式HPを参考にしてください。
python-docx.readthedocs.io