AcademicWordListをpythonでpdfから抽出した話

# TL;DR

pdfminer3kを用いてpdfから単語一覧と対応するリスト番号を抽出します。

# 成果物

https://github.com/musaprg/celese_tools/blob/master/pour_words_from_pdf/pour.py

# pdfminer3kとは

今回使用したのは、pdfファイルから情報を抽出することのできる`pdfminer3k`というライブラリです。正確には、`pdfminer`というライブラリのPython3対応バージョンです。

これを使うと、htmlのスクレイピングのような要領で、pdfから情報を簡単に抽出することができます。

# 手順

## インストール

PyPIに登録されてるので、サクサクとインストールできます。

```
pip install pdfminer3k
```

## 抽出&CSV出力

[こちらのサイト](http://cartman0.hatenablog.com/entry/2017/08/26/022957)に掲載されているコードを参考にさせていただきました。

```python:extractor.py
from argparse import ArgumentParser
import re

from pdfminer.pdfparser import PDFParser
from pdfminer.pdfparser import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfparser import PDFPage
from pdfminer.pdfdevice import PDFDevice
from pdfminer.converter import PDFPageAggregator
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.layout import LTTextBoxHorizontal

def main(path_to_pdf):
    fp = open(path_to_pdf, 'rb')

    parser = PDFParser(fp)
    document = PDFDocument()
    parser.set_document(document)

    password=""
    document.set_parser(parser)
    document.initialize(password)

    rsrcmgr = PDFResourceManager()

    laparams = LAParams()

    device = PDFPageAggregator(rsrcmgr, laparams=laparams)
    intepreter = PDFPageInterpreter(rsrcmgr, device)

    pages = list(document.get_pages())

    words = []
    numbers = []

    for i,page in enumerate(pages):
        print("---------page %d----------" % i)
        intepreter.process_page(page)

        layout = device.get_result()

        for l in layout:
            if isinstance(l, LTTextBoxHorizontal):
                texts = re.sub(r"\s+", " ", l.get_text())
                texts = texts.strip("\n")
                texts = texts.split(" ")
                texts = [t for t in texts if t != '']
                numbers = numbers + [int(t) for t in texts if t.isdigit()]
                words = words + [t for t in texts if not t.isdigit()]

    print(numbers)
    print(words)

    save_as_csv(words, numbers)

def save_as_csv(words, numbers):
    import csv

    with open("output.csv", "w") as f:
        writer = csv.writer(f, lineterminator='\n')
        header = []
        header.append("word")
        header.append("list_number")

        writer.writerow(header)

        for a,b in zip(words, numbers):
            writer.writerow([a,b])

if __name__ == '__main__':
    parser = ArgumentParser(
            description='Unpacking word lists from pdf files.')
    parser.add_argument("path_to_pdf",
            type=str,
            help='Path to pdf files')
    args = parser.parse_args()
    main(args.path_to_pdf)
```

すると、以下のpdfから、単語と、その単語が属するリスト番号がペアになってcsvとして吐きだされます。

https://github.com/musaprg/celese_tools/blob/master/pour_words_from_pdf/awl.pdf

# pdfminer3kを使用する上での注意事項

pdfは、htmlと違ってあまりキレイに構造化されていません。

レイアウト重視のpdfの場合は、抽出する際に、予想される順序で抽出がされない場合がほとんどです。

今回は、抽出されたテキストを

1. 空白や改行文字を除去する
2. 単語や数字ごとに分割して配列に格納
3. 配列を全走査して数字と文字の配列にそれぞれ分割
4. csv出力時にzipで結合する

といった感じでうまく処理しています。

これは、pdfによるので、まずは対話シェル等で、どのように抽出されるかをチェックしてから、事後処理の方法を検討するとうまくいくと思います。

## 参考文献

https://sitest.jp/blog/?p=12170
http://cartman0.hatenablog.com/entry/2017/08/26/022957