Python:青空文庫「銀河鉄道の夜」のセリフ部分のみを抜き出す

Pythonのロゴ Python

宮沢賢治は「グスコーブドリの伝記」と「銀河鉄道の夜」がとりわけ大好きだ。
「銀河鉄道の夜」は、これまで何度も読み返している。アニメ映画で以前上映された「銀河鉄道の夜」も見た事がある。(なぜかカムパネルラやジョバンニたちキャラクターが猫に置き換えられていて印象に残っている)

著作権切れの小説は青空文庫で読むことが出来る、ということを最近思い出した。青空文庫は、有志の手により入力や校正がなされている貴重なサイトの一つだ。ネット上で読めるならばデータとしてあるわけなので、プログラムで小説を取り出せないかと考えた。

やろうとした事のイメージ

例えば以下のような原文

【原文】
先生は意外なようにしばらくじっとカムパネルラを見ていましたが、急いで「では。よし。」と云いながら、自分で星図を指さしました。
「このぼんやりと白い銀河を大きないい望遠鏡で見ますと、もうたくさんの小さな星に見えるのです。ジョバンニさんそうでしょう。」
ジョバンニはまっ赤になってうなずきました。けれどもいつかジョバンニの眼のなかには涙なみだがいっぱいになりました。

こんな感じで抽出したい。

【抽出結果】
「では。よし。」
「このぼんやりと白い銀河を大きないい望遠鏡で見ますと、もうたくさんの小さな星に見えるのです。ジョバンニさんそうでしょう。」

「晩御飯食べた?」のようなカギ括弧でくくられた部分は、登場人物たちの会話部分だということが突如として頭に浮かび、小説のセリフ部分のみ取り出せないかと考えて作ったプログラムが以下だ。

青空文庫「銀河鉄道の夜」のセリフ部分のみを抜き出すPythonコード

gingastation.py

# -*- coding: utf-8 -*-

"""
	青空文庫の「銀河鉄道の夜」からセリフ箇所(「~」)のみ抜き出す

	例)

	【原文】

		先生は意外なようにしばらくじっとカムパネルラを見ていましたが、急いで「では。よし。」と云いながら、自分で星図を指さしました。
		「このぼんやりと白い銀河を大きないい望遠鏡で見ますと、もうたくさんの小さな星に見えるのです。ジョバンニさんそうでしょう。」
		ジョバンニはまっ赤になってうなずきました。けれどもいつかジョバンニの眼のなかには涙なみだがいっぱいになりました。

	【抽出結果】
		「では。よし。」
		「このぼんやりと白い銀河を大きないい望遠鏡で見ますと、もうたくさんの小さな星に見えるのです。ジョバンニさんそうでしょう。」

"""

import urllib.request
from bs4 import BeautifulSoup		# BeautifulSoup4を利用してテキストのみ取り出すため
import re										# 正規表現を利用するため

# 青空文庫「銀河鉄道の夜」のURL
url = "https://www.aozora.gr.jp/cards/000081/files/456_15050.html"

# サイトデータをオブジェクトとして取得
data = urllib.request.urlopen(url)

# プログラムからHTMLとして扱えるように変換
soup = BeautifulSoup(data, 'html.parser')

# 本文(bodyタグ部分)のみ取り出す
body = soup.find("body")

# かぎ括弧でくくられた部分を取り出すための正規表現(最短マッチ、改行文字も文字に含む)
regex = re.compile("「.*?」", flags=re.DOTALL )

# 一致した部分をリスト形式で取り出す
matchArray = regex.findall(body.text)

# 取得したセリフを全て出力
for i in range(len(matchArray)):
	print("{0}".format(matchArray[i] ) )

実行結果

「ではみなさんは、そういうふうに川だと云(い)われたり、乳の流れたあとだと云われたりしていたこのぼんやりと白いものがほんとうは何かご承知ですか。」
「ジョバンニさん。あなたはわかっているのでしょう。」
「大きな望遠鏡で銀河をよっく調べると銀河は大体何でしょう。」
「ではカムパネルラさん。」
「では。よし。」
「このぼんやりと白い銀河を大きないい望遠鏡で見ますと、もうたくさんの小さな星に見えるのです。ジョバンニさんそうでしょう。」
:
:
(略)

今回取り出した「銀河鉄道の夜」のセリフ全文は以下にあります。

解説

サイトデータ取得にurllib.request、インポートしたデータの抽出にbs4(BeautifulSoup4)、正規表現を使ってカギ括弧内の文字を取得したため、reモジュールをそれぞれインポート。

import urllib.request
from bs4 import BeautifulSoup
import re

まず、青空文庫で検索した「銀河鉄道の夜」のURLをチェックして変数に設定。(要するに文章が表示されているサイトURLなら何でもOK)

サイトデータは、urllib.request.urlopenを使うと簡単に取り出せる。

data = urllib.request.urlopen(url)

urllib.request.urlopenで取り出したサイトデータはファイルオブジェクトのようなデータのため、そのままでは扱えない。これをHTMLのタグ名などから抽出できるようにbs4の機能でデータ変換。

soup = BeautifulSoup(data, 'html.parser')

更にbs4の機能で、本文(bodyタグ部分)のみを取得。(bs4非常に便利です!)

body = soup.find("body")

正規表現を使うと、カギ括弧でくくられた部分を取り出すことが可能。
まずは、検索したい文字列のパターンを作成。
以下の最初の引数“「.*?」”の部分がパターン。
ちなみに.*?の部分は「0文字以上の文字を含むカギ括弧」という意味。

regex = re.compile("「.*?」", flags=re.DOTALL )

実はこの「.*?」の部分は、当初「.*」としていた。
しかし、以下のような文章だと

しばらくして海の中で起き上がるように姿勢を改めた先生は、「もう帰りませんか」といって私を促した。比較的強い体質をもった私は、もっと海の中で遊んでいたかった。しかし先生から誘われた時、私はすぐ「ええ帰りましょう」と快く答えた。そうして二人でまた元の路みちを浜辺へ引き返した。

こう取り出されてしまう。

「もう帰りませんか」といって私を促した。比較的強い体質をもった私は、もっと海の中で遊んでいたかった。しかし先生から誘われた時、私はすぐ「ええ帰りましょう」

要するに最初のと最後のまでの扱いになってしまうのだ。
そこで?をつけ「.*?」というパターンに落ち着いた。(最短マッチと呼ばれるらしい)
これできちんと以下のように取り出せるようになった。

「もう帰りませんか」
「ええ帰りましょう」

この行では、更にもう一つポイントがあって、最後のflags=re.DOTALLのフラグ設定部分。

regex = re.compile("「.*?」", flags=re.DOTALL )

flags=re.DOTALLの意味は正規表現の.に対して(ちなみに.任意の1文字を表す)改行も文字に含めた設定にするという意味。
実はこのflags=re.DOTALLの設定が無いと、改行も含めた「」でくくられた文章が取り出せない。

例えば

「おや
なんていい香りだろう」

みたいな複数行にわたるセリフが取り出せないのだ。(幸い、今回の「銀河鉄道の夜」では改行を含むセリフは無かった。一応他の小説では、改行を含むセリフが存在していたためflags=re.DOTALLを追加した次第だ)

上記パターンを使って本文から全てのセリフ部分を抜き出す。
regex.findallを使うと戻り値がリスト形式となるため、変数matchArraymatchArray[1]のように使う。

matchArray = regex.findall(body.text)

最後に取得したリスト変数(matchArray)を全て表示する。
for文の範囲を

for i in matchArray:

としたいところだが、こうしてしまうと変数iには数値が文字列として取り出されてしまうため範囲をrange(len(matchArray))として数値を変数iに取り出すようにしている。

for i in range(len(matchArray)):
	print("{0}".format(matchArray[i] ) )

個人的には、田中芳樹の「銀河英雄伝説」なんかのセリフを取り出してみたいところですが、まだ著作権切れではないですし、テキスト化もされていませんよね。

以上、Pythonを使って青空文庫「銀河鉄道の夜」のセリフ部分のみを抜き出すでした。

コメント

タイトルとURLをコピーしました