Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

COTOHAを利用して、物語の舞台を抽出・図示してみた

More than 1 year has passed since last update.

概要

物語の舞台が地元だったりすると、それだけで読んでしまうタイプなので
COTOHAの固有表現抽出を、利用して物語の舞台を、抽出・図示するものを作成してみた。

処理の流れ

1.青空文庫より任意の小説を取得
2.COTOHA API(固有表現抽出)で地名(LOC)のみ取得
3.取得した地名を利用して、WordCroudで図示する。

環境

Google Colaboratory

ライブラリ

・個別にインストールが必要なものはwordcloudのみ。
 (Google Colaboratoryは事前にある程度のライブラリがインストール済みなので、便利です)
 
 以下のコマンドを実行すれば、事前準備は完了です。

wordcloudのインストール&フォントのダウンロード
!pip install wordcloud
!apt-get -y install fonts-ipafont-gothic
!rm /root/.cache/matplotlib/fontlist-v300.json
青空文庫をクローン
!git clone --branch master --depth 1 https://github.com/aozorabunko/aozorabunko.git

コード

1.青空文庫より任意の小説を取得する部分
青空文庫より任意の小説を取得する部分
from bs4 import BeautifulSoup

def get_word():

  #クローンしたhtmlからパスを指定(サンプルは太宰治のグッド・バイ)
  path_to_html='aozorabunko/cards/000035/files/258_20179.html'

  #BeautifulSoupでhtml解析
  with open(path_to_html, 'rb') as html:
    soup = BeautifulSoup(html, 'lxml')
  main_text = soup.find("div", class_='main_text')
  for yomigana in main_text.find_all(["rp","h4","rt"]):
    yomigana.decompose()
  sentences = [line.strip() for line in main_text.text.strip().splitlines()]
  aozora_text=','.join(sentences)

  #cotoha apiコール用に文字数で分割(1800文字ごと)
  aozora_text_list = [aozora_text[i: i+1800] for i in range(0, len(aozora_text), 1800)]
  return aozora_text_list


Gitからクローンした青空文庫より、任意の小説のパス(path_to_html)を指定して全文取得し
BeautifulSoupでパースしてます。
(ちなみに、サンプルは太宰治のグッド・バイ)

また、COTOHAが実行できるように、文字列を1800文字ごとに分割して配列化してます。
(ちゃんと調べてないですが2000文字でダメで、1800文字で実行したらイケたので。。ちゃんと調べろよ


2.COTOHA_APIを呼び出しする部分
COTOHA_APIをコールする部分
import os
import urllib.request
import json
import configparser
import codecs
import sys
import time


client_id = "各自のクライアントID"
client_secret = "各自のシークレットキー"

developer_api_base_url = "https://api.ce-cotoha.com/api/dev/nlp/"
access_token_publish_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"

def cotoha_call(sentence):
    # アクセストークン取得
    def getAccessToken():     
        url = access_token_publish_url
        headers={
            "Content-Type": "application/json;charset=UTF-8"
        }
        data = {
            "grantType": "client_credentials",
            "clientId": client_id,
            "clientSecret": client_secret
        }
        data = json.dumps(data).encode()
        req = urllib.request.Request(url, data, headers)
        res = urllib.request.urlopen(req)
        res_body = res.read()
        res_body = json.loads(res_body)
        access_token = res_body["access_token"]
        return access_token

    # API URL指定(固有表現抽出)
    base_url_footer = "v1/ne" 
    url = developer_api_base_url + base_url_footer
    headers={
        "Authorization": "Bearer " + getAccessToken(), #access_token,
        "Content-Type": "application/json;charset=UTF-8",
    }
    data = {
        "sentence": sentence
    }
    data = json.dumps(data).encode()
    time.sleep(0.5)
    req = urllib.request.Request(url, data, headers)

    try:
        res = urllib.request.urlopen(req)
    # リクエストでエラーが発生した場合の処理
    except urllib.request.HTTPError as e:
        # ステータスコードが401 Unauthorized or 500 Internal Server Errorならアクセストークンを取得し直して再リクエスト
        if e.code == 401 or 500:
            access_token = getAccessToken()
            headers["Authorization"] = "Bearer " + access_token
            time.sleep(0.5)
            req = urllib.request.Request(url, data, headers)
            res = urllib.request.urlopen(req)
        # 401 or 500 以外のエラーなら原因を表示
        else:
            print ("<Error> " + e.reason)
            #sys.exit()

    res_body = res.read()
    res_body = json.loads(res_body)
    return res_body

COTOHA(固有表現抽出)を呼び出す部分、エラー401と500の場合のみリトライするようにしてます。


3.WordCloudで図示する部分
WordCloudで図示する部分
from wordcloud import WordCloud
import matplotlib.pyplot as plt
from PIL import Image

def get_wordcrowd_mask(text):

  #日本語フォント指定
  f_path = '/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'

  #wcパラメタ指定
  wc = WordCloud(background_color="white",
                  width=500,
                  height=500,
                  font_path=f_path,
                  collocations=False,
                  ).generate( text )

  #画面描写
  plt.figure(figsize=(5,5), dpi=200)
  plt.imshow(wc, interpolation="bilinear")
  plt.axis("off")
  plt.show()

WordCloudを利用して、テキストを図示する部分です。


4.メイン
処理を実行する部分
aozora_text_list = get_word()
json_list = []
loc_str = ''
cnt = 0
for i in aozora_text_list:
  cnt+=1
  print( str(cnt) + '/' + str(len(aozora_text_list)) )
  json_list.append(cotoha_call(i))


for i in json_list:
  for j in i['result']:
    if(j['class'] == 'LOC'):
      loc_str = loc_str + j['form'] + ","

get_wordcrowd_mask(loc_str)

1~3を単純に実行している部分
また、APIコール時の進捗を、以下出力結果のように表出してます。(n/1でテキスト分割時の配列内の個数)

1/9
2/9
3/9
4/9
5/9
6/9
7/9
8/9
9/9

出力結果

・グッド・バイ(太宰治)

goodbye.png

すこし、地名と関係なさそうな文字が混じってはいますが、概ね抽出できてそうですね。
以下、他にも試してみた結果。

・斜陽(太宰治)

syayou.png

・銀河鉄道の夜(宮沢賢治)

gingatetudou.png

・檸檬(梶井基次郎)

REMON.png

なんか楽しいな。。。

まとめ

COTOHAもColabも無償で使用できて、手軽に言語処理に触れる
環境なのですごいいいですね!

以上です、ご一読ありがとうございました!
 
 

 
 
 
 
 

最後に、以下画像は何の作品でしょう!(うざいからやめろ。。)
gongitune.png

tanaka-kazuki
青森大好きエンジニア
jenesty
飯田橋に本社を構えるIT企業『株式会社 ジェネスティコンサルティング』のコミュニティです。ITエンジニア向けコワーキングスペース『JeneLab』(ジェネラボ)の運営もしています → https://jenelab.tech
https://jenesty.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away