モチベーション
とある大学事務の人から、科研費申請書をチェックするのが大変だという話を聞きました。現在は手で1部ずつ赤入れをしているそうです。
- 公募要領に沿って記入しているか
- 図や参考文献は適切に入っているか
- 業績リストは適切な形式になっているか
など、自動チェックツールがあると良さそうです。
Pythonで自動チェックツールを作れるといいですね! と思いつつ、すぐ作るのは荷が重いので、まずはPythonからのWordファイルの読み書きを試してみました。
科研費申請書に準じるサンプルとして、筆者が2011年6月に提出した日本学術振興会特別研究員(学振)DC2の申請書を読んでみました。学振についてよく知らない人は、身近な博士課程出身者に聞いてみると感情のこもった応答が返ってくるかもしれません。
PythonからWordファイルってどうやって読むの?
Read from a word file in python
主にpython-docxとdocx2txtがあり、どちらも.docxファイルのみに対応しています。後述しますが、.docファイルを読む時は、antiwordによる.docxへの変換が必要になります。
docx2txtはヘッダ・フッタ・ハイパーリンクからもテキストを読み取れるということなので、今回は主にdocx2txtで試してみました。
環境
- MacOS Mojave 10.14.5
- Anaconda 2020.02
- Python 3.7.6
- Jupyter Notebook 6.0.3
python-docxのインストール
pip install python-docx
python-docxはPython 3.4までしか対応していないようですが、Python 3.7で動くことは動きました。AnacondaにPython 3.4が入らないので、3.7のままにしました。
docx2txtのインストール
pip install docx2txt
antiwordのインストール
後述しますが、サンプルとして読みたかったWordファイルが.docxではなく.doc形式でした。
python-docxで.doc形式のファイルは開けません。
Wordで開いて.docxとして保存するのはなんか負けたような気がするので、antiwordでの変換を試みました。
apt-getでインストール:失敗
結論から言うと、Macでapt-getでのantiwordのインストールはできませんでした。
antiwordはapt-getで入れればいいのか、と思ってfinkを入れ、finkのインストール途中でJDKがないと言われてなぜかAdobe Flashplayerのダウンロードページに飛ばされたりしました(もちろんFlashplayerをインストールしても何も解決しませんでした)。
sudo apt-get antiword
E: Invalid operation antiword
brewでインストール:成功
brew install antiword
ここを見てbrewコマンドを入れたら無事インストールできました。
(base) akpro:~ kageazusa$ antiword
Name: antiword
Purpose: Display MS-Word files
Author: (C) 1998-2005 Adri van Os
Version: 0.37 (21 Oct 2005)
Status: GNU General Public License
Usage: antiword [switches] wordfile1 [wordfile2 ...]
Switches: [-f|-t|-a papersize|-p papersize|-x dtd][-m mapping][-w #][-i #][-Ls]
-f formatted text output
-t text output (default)
-a <paper size name> Adobe PDF output
-p <paper size name> PostScript output
paper size like: a4, letter or legal
-x <dtd> XML output
like: db (DocBook)
-m <mapping> character mapping file
-w <width> in characters of text output
-i <level> image level (PostScript only)
-L use landscape mode (PostScript only)
-r Show removed text
-s Show hidden (by Word) text
2005年のツールなんですね!
python-docxでの読み書きテスト
こちらの後半のコードをコピペしたら動き、Wordファイルの作成・読み取りができました。
Python 3.7で特に問題はないようです。
なお、こちらのコメント欄にあるコードをコピペして動かしてみたら、docx_simple_service
は読み込めませんでした。たぶんPythonのバージョンによるものだと推定しています。
ModuleNotFoundError: No module named 'docx_simple_service'
antiwordで.docファイルを.docxファイルに変換してdoc2txtで読む
いよいよサンプルを読んでみます。
サンプル
このような申請書を使います。最近はもうなくなった枠線が現役だった時代です。.docファイルなので、そのままではPythonで読めません。
最終版のWordファイルが発掘できなかったので、メール提出して事務チェックしてもらった最終よりちょっと前のバージョンを使います。
.docファイルの読み取り
こちらの回答にある関数ですぐ読めました。
pathの指定部分だけ微妙に変えてあります。
.docファイルをantiwordで.docxファイルに変換して読み、読んだ.docxファイルはすぐ消しています。
import os, docx2txt
def get_doc_text(filepath, file):
if file.endswith('.docx'):
text = docx2txt.process(file)
return text
elif file.endswith('.doc'):
# converting .doc to .docx
doc_file = os.path.join(filepath, file)
docx_name = file + 'x'
docx_file = os.path.join(filepath, docx_name)
if not os.path.exists(docx_file):
os.system('antiword ' + doc_file + ' > ' + docx_file)
with open(docx_file) as f:
text = f.read()
os.remove(docx_file) #docx_file was just to read, so deleting
else:
# already a file with same name as doc exists having docx extension,
# which means it is a different file, so we cant read it
print('Info : file with same name of doc exists having docx extension, so we cant read it')
text = ''
return text
小見出しごとの内容を抽出してみる
読んだテキストを使って、小見出しごとの内容を抽出してみたいと思います。
今回の例では、小見出しは【】で括られています。【問題点】とか。
テキストの整形
改行などを削除します。
gakushin = get_doc_text('./sample', '110624GakushinDraftKAGE2.1.doc')
gakushin = gakushin.replace('\n', '').replace('|', '').replace('\u3000', '')
まだ連続スペースが残っているので、ここを見ながら削除します。
import re
# 連続スペースを削除して半角スペース1個にする
gakushin = re.sub('[ ]+', ' ', gakushin)
et al. と年号の間など、できればスペースが残ってほしいところもあるので、とりあえず半角スペース1つは残しました。厳密にやるならスペース全部削除した後にet al. とか & の周りだけ置換するのがよいと思います。
小見出しの抽出
【】で括られた部分を検索してみます。正規表現弱者なので、検索してここを参考にしたら動きました。
re.findall('\【.+?\】', gakushin)
['【背景】',
'【問題点】',
'【解決方策、研究目的、研究方法、特色と独創的な点】',
'【研究経過1】',
'【研究経過2】',
'【これからの研究計画の背景】',
'【問題点・解決すべき点】',
'【着想に至った経緯】',
'【2-1】',
'【2-2】',
'【査読あり】',
'【口頭発表・査読なし】',
'【ポスター発表・査読なし】',
'【研究職を志望する動機】',
'【目指す研究者像】',
'【自己の長所等】',
'【特に優れた学業成績,受賞歴】',
'【特色ある学外活動】']
小見出しが抽出できました!
小見出しの下の文章の抽出
小見出しを変数に格納して、小見出し自体を使ってテキストgakushin
を分割してみます。
subhead = re.findall('\【.+?\】', gakushin)
text = gakushin
split_result = []
for i in range(len(subhead)):
new_text = text.split(subhead[i])
split_result.append(new_text[0])
text = new_text[1]
# 最後の1回だけ[1]を入れる
split_result.append(new_text[1])
文章を小見出しで分割してリスト化できました。要素数を調べてみます。
print('小見出しの要素数', len(subhead))
print('分割した文章の要素数', len(split_result))
小見出しの要素数 18
分割した文章の要素数 19
小見出しの要素数+1 = 小見出しで分割した文章の要素数 となり、計算は合っているようです。
小見出しとその下の文章が合うように、pandas DataFrameに格納してみます。リストsplit_result
の最初の要素は捨てることになります。
import pandas as pd
df = pd.DataFrame([subhead, split_result[1:19]]).T
df.columns = ['subhead', 'text']
小見出しとその下の文章が対応づけられました。文字数を数えてデータフレームに入れてみます。
df['length'] = df.text.apply(len)
【2-2】という項目が特に長いようです。これだけ見ても【2-2】が何を表す見出しなのかはよくわかりません。研究計画っぽいですが、【1】がない理由は不明です。
まとめ
Pythonから.docファイルを読み取り、テキストを操作することができました。
今後いろいろ試していきたいと思います。
参考
- 特別研究員|日本学術振興会
- Read from a word file in python
- python-docx
- docx2txt
- Read .doc file with python
- Install antiword on Mac OSX
- Python で excel・wordの読み書き
- 【Python】【Word】【python-docx】python-docxを使ってPythonでワード文章のひな形を作成してみる
- 研究者、ツイッターで政治を動かす?
- Reading .doc file in Python using antiword in Windows (also .docx)
- 連続するスペースを削除して1つにするpythonのコード
- かっこで囲まれた文字を検索する