太陽がまぶしかったから

C'etait a cause du soleil.

Python と MeCab でオライリー出版の EPUB 電子書籍を日本語形態素解析する

退屈なことはPythonにやらせよう 第2版 ―ノンプログラマーにもできる自動化処理プログラミング

O'Reilly の電子書籍をテキストマイニング

 上記の環境を整えたかった理由のひとつとして電子書籍のテキストマイニングがある。

 Amazon Kindle や 楽天 Kobo などの電子書籍データは暗号化されているため正当な方法で抽出することができないが、オライリー出版の電子書籍は DRM フリーの EPUB 形式で配信されている。

EPUBは、HTMLやウェブブラウザのオープン性を保持しつつ、インターネット接続が切断された状態の携帯情報端末(PDA)やノートパソコンなどでも電子書籍の閲覧が継続できるようにダウンロード配信を前提にパッケージ化された、XHTMLのサブセット的なファイル・フォーマット規格であり、画面の大きさに合わせて表示を調整する「リフロー機能」が特徴である(固定する設定も可能)。

 EPUB はXHTMLのサブセットをZIPで固めたものであるため、一般的なウェブサイトスクレイピングの手法を応用することで比較的簡単に解析できる。

EbookLib で EPUB を読み出して頻出単語を解析

 今回のプログラムには EbookLib を利用する。 Kindle フォーマット解析も開発中とあるが、今のところは EPUB2 と EPUB3 に対応している。 requirements.txt に以下のモジュールを追加して pip3 install -r requirements.txt を実行

lxml==4.6.3
ebooklib==0.17.1

 以下のプログラムで利用できる。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__version__ = "0.1.0"

import argparse
import logzero
import settings
import ebooklib
from ebooklib import epub
import re
import mecab_tokenize


class EpubTokenize:
    def __init__(self):
        logzero.logfile(
            settings.logfile,
            loglevel=20, maxBytes=1e6, backupCount=3
        )
        self.logger = logzero.logger
        self.mecab = mecab_tokenize.MecabTokenize()

    def extract_epub_text(self, path):
        text = ''
        for item in epub.read_epub(path).get_items():
            if item.get_type() == ebooklib.ITEM_DOCUMENT:
                text += re.sub(r'<.+?>', '', item.get_content().decode())
        return text

    def freq(self, text):
        df = self.mecab.freq(text, count=10)
        return df[df['info1'].isin(['名詞', '動詞']) & (df['term'].str.len() >= 2)]

    def main(self, args):
        self.logger.info(f'{__file__} {__version__} {args}')
        text = self.extract_epub_text(args.path)
        df = self.freq(text)
        df.to_csv(args.path + '.freq', sep='\t')


if(__name__ == '__main__'):
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--version',
        action='version', version=f'{__version__}'
    )
    parser.add_argument('path')
    args = parser.parse_args()
    EpubTokenize().main(args)

 extract_epub_text() でテキストを抽出して正規表現でタグを除去。freq() で10個以上カウントされた単語を pandas.DataFrame 形式で取得して、さらに2文字以上の名詞と動詞で単語を絞り込んでいる。 mecab_tokenize についてはこちらを参照のこと。

オライリー出版の EPUB 電子書籍の頻出単語を表示

% docker-compose run --entrypoint "python /data/src/epub.py" mecab-test '../work/oreilly_test.epub'
Creating mecab-test_mecab-test_run ... done
[I 210530 18:18:39 epub:35] /data/src/epub.py 0.1.0 Namespace(path='../work/oreilly_test.epub')
% head work/oreilly_test.epub.freq
        term    info1   info2   freq
6       gt      名詞    固有名詞        4298
10      する    動詞    自立    2731
12      ()      名詞    固有名詞        1775
14      よう    名詞    非自立  1160
15      こと    名詞    非自立  1085
17      プログラム      名詞    サ変接続        977
21      ファイル        名詞    一般    755
23      文字列  名詞    固有名詞        678
25      でき    動詞    自立    659

 gt は本文内の「> 」なので除去対象にしても良いかもしれない。ここまででオライリー出版の EPUB 電子書籍の形態素解析ができた。書籍内に出てくる単語の頻度を解析することで、その文章のジャンルを推定したり、新しい用語が広がっていった過程を知ることができる。簡単なスクリプトなので必要に合わせたカスタマイズも容易だ。

EPUB形式の電子書籍を解析対象として保有する意義

 例えば『お好み焼きの戦前史 第二版』においても過去文献類をスキャンしたり、ネット上の文献をダウンロードすることで作成した電子テキストデータ群を解析することで料理方法の初出や普及の過程を明らかにしており、電子テキストデータ群の準備こそが重要な研究材料となっている。

 基本的には Amazon Kindle のロックインから離れられるとは思えないけれど、解析対象としての電子書籍所有という観点の場合は EPUB 形式のがありがたい。そもそも編集作業はデジタルで行なっているのが当たり前になってきているのに電子テキストデータが隠されているのは残念な状況。

 もちろん、簡単にコピーできないようにするのも必要な措置だが、例えば国会図書館への寄贈については最終稿のデータ入稿もセットで求めていかないと無駄な再デジタル化コストやOCRの誤判定などによる研究の非効率化が出てきてしまうのではないかと思えた。これからは、EPUB形式の電子書籍を解析対象として保有する意義についても意識したい。