"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

複数行にわたる文章をAIで1行に要約をするデモ画面。「Text Summarization API」を使う/django2.0+bootstrap4

今回は、文章を入力して1行に要約された結果を返すデモ画面を作ってみます。

f:id:arakan_no_boku:20190320212948j:plain

 

この記事の前提

 

djangoのインストール・設定や、bootstrapを使うための設定などは、以下で説明している内容を前提にしています。

不明点があれば、以下も参照ください。

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

 

Text Summarization API

 

今回は、 A3RTの「Text Summarization API」を使います。

こちらのページに解説があります。

a3rt.recruit-tech.co.jp

概要を引用します。

Text Summarization APIは、文章要約を行うAPIです。
入力された文章それぞれの意味を読み取り、特徴的な文章を抽出することができます。

利用するには「API KEY」が必要なので、上記のページから取得しておきます。

メールアドレスを入力したら、メールで送られてくるので、手続き的には簡単です。

 

デモ画面のイメージから

 

入力画面はこんな感じ。

f:id:arakan_no_boku:20190320221235j:plain

単純に複数行入力エリアに、要約元の文章を貼り付けて、送信ボタンを押すと。

f:id:arakan_no_boku:20190320221548j:plain

1行に要約された文章を青いボックスに表示する。

とまあ。

これだけです。

 

 

上記のソースコードです

 

まず、入力するテキストエリアを定義します。

 

forms.py
from django import forms

class UserForm(forms.Form):
     textone = forms.CharField(label='要約元文章',max_length=200,min_length=20,widget=forms.Textarea(attrs={'id': 'commu'}))

APIの制約として「要約できる1文の最大文字数は200文字、且つ最大文章数は10です。」とあるので、とりあえずmax-length=200でしばりをいれてます。

あまり短すぎる文章も意味がないので、min-lengthは20にしてます。

 

views.py
from django.http.response import HttpResponse
from django.shortcuts import render, render_to_response
from django.contrib.staticfiles.templatetags.staticfiles import static
from . import forms
from django.template.context_processors import csrf
from . import apido


def demo(request):
    api = apido.Apido()
    if request.method == 'POST':
        # テキストボックスに入力されたメッセージ
        textone = request.POST["textone"]
        # APIリクエストを投げてからの応答を取得
        rets = api.get(textone)
        c = {
             'textone':textone,
             'rets':rets
        }
        return render(request,'result.html',c)
    else:
        # 初期表示の時にセッションもクリアする
        request.session.clear()
        # フォームの初期化
        form = forms.UserForm(label_suffix=':')
        c = {'form': form}
        c.update(csrf(request))
        return render(request,'demo02.html',c)

APIを使って要約文を取得する処理は「apido.Apido」クラスに集約します。

画面のテキストエリアの入力を 「 textone = request.POST["textone"]」で受けて、要約結果のレスポンスを「rets = api.get(textone)」で受け取るって感じです。

入力画面を「demo02.html」でうけて、要約結果を「result.html」で表示します。

 

apido.py
import requests
import json
import urllib.parse

class Apido:
    def __init__(self):
        self.key = 'DZZXXXXXXX8'
        self.api = 'https://api.a3rt.recruit-tech.co.jp/text_summarization/v1'

    def get(self,inputtext):
        url = self.api
        quoted_text = inputtext
        print(quoted_text)
        r = requests.post(url,{'apikey':self.key,'sentences':quoted_text,'linenumber':1,'separation':'。'})
        data = json.loads(r.text)
        if data['status'] == 0:
            ret = data['summary']
        else:
            ret = ['サマリが取得できませんでした。']
        return ret

指定のURLとオプションでPOSTします。 

基本は「sentences」オプションで 要約する文章をリクエストして、レスポンスの「summary」で要約済のテキストを得ます。

レスポンスを扱いやすくするため、一度JSON形式にして、キー指定「data['summary']」でとりだしているわけです。

処理状況は「 data['status'] 」が0(OK)かどうかで判断し、エラーなら「サマリが取得できませんでした」というエラーメッセージを返します。

 

urls.py
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('demo/',views.demo,name='demo02'),
]

demo/で入力画面を表示するURLを定義します。

viewsフォルダの「demo02.html」を表示します。

 

demo02.html

 

HTMLです。

{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% load widget_tweaks %}

{% block header %}

{% endblock %}

{% block title %}
  チャットボットもどき
{% endblock %}

{% block content %}
<div class="container">

    <form action="" method="post">{% csrf_token %}
        <h1>文章の要約</h1>
        <div class="form-group row my-4">         
            <label class="col-lg-2 col-form-label"><h4>{{form.textone.label}}</h4></label>
    		<div class="col-lg-6">       
	            {{form.textone|add_class:"form-control"}}
    		</div>
    		<div class="col-lg-2">       
	            <button type="submit" class="btn btn-primary">送信する</button>
    		</div>
        </div>
     </form>
 </div>  
{% endblock %}

forms.pyで定義した名称「textone」で参照しています。

今度は、結果を表示する画面です。

 

result.html

 

HTMLです。

{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% load widget_tweaks %}

{% block header %}
<link rel="stylesheet" href="{% static "css/dj_talk.css" %}"></link>
{% endblock %}

{% block title %}
  チャットボットもどき
{% endblock %}

{% block content %}
<div class="container">

    <form action="" method="post">{% csrf_token %}
        <h1>文章の要約</h1>
        <div class="form-group row my-4">         
            <label class="col-lg-2 col-form-label"><h4>元文章</h4></label>
    		<div class="col-lg-6">       
	            <p class="card-text">{{ textone}}</p>
    		</div>
         </div>
     </form>
        <div id="resultarea">
        {% for txt in rets %}
        	<div class="form-group row my-2">         
	        	<div class="col-lg-2"><img src="{% static "images/k.png" %}" /></div> 
     			<div class="p-3 m-1 col-lg-6 bg-primary text-white">       
	           		{{ txt }}
	        	</div> 
        	</div>
        {% endfor %}
        </div>
</div>  
{% endblock %}

ちょっと補足です。

talk.cssを読み込んでますが、これは、こちらでチャットボット画面につかったものを流用しています。

arakan-pgm-ai.hatenablog.com

これで一応、実行できるはずです。

 

おまけ:URLエンコードに関する試行錯誤

 

ここまでで、一応できているんですが、どうも引っ掛かってるところがあります。

それは、このページに書いてある制限事項のURLエンコードの記述です。

https://a3rt.recruit-tech.co.jp/product/TextSummarizationAPI/

要約できる1文の最大文字数は200文字、且つ最大文章数は10です。
「&」記号や半角スペースがある場合には、URLエンコードをしてください(curlでは-data-urlencodeを使用) 。
入力する文章に文字の切れ目となるseparationを必ずいれてください。デフォルトのseparationは「。」です。デフォルトの場合は「。」を必ず文章に入れてください。 

ここに”「&」記号や半角スペースがある場合には、URLエンコードをしてください。”と書いてあるのですが・・どういう意味か、いまいちわかりません。

何故かというと。

&記号や半角スペースはURLエンコードするけど、他はしなくてもよい?という意味かと思ってやってみたんですね。

 

apido.pyで&と半角スペースだけURLエンコードする

 

抜粋です。

    encode_dic = {'&':'%26',' ':'%20'}
    enc_table = str.maketrans(encode_dic)
    quoted_text = inputtext.translate(enc_table)
    r = requests.post(url,{'apikey':self.key,'sentences':quoted_text,'linenumber':1,'separation':'。'})

辞書に定義した置換をまとめて行えるpythonのtranslate()を使って変換したものでやってみました。 

文章の頭に「& & &」みたいにいれて送信すると。

f:id:arakan_no_boku:20190321145007j:plain

うーーん。

なんだかおかしいな。

 

インプット全体をURLエンコードしてみる

 

じゃあ・・・、ということで、入力された文章全体をURLエンコードしてみました。

     quoted_text = urllib.parse.quote(inputtext)
     r = requests.post(url,{'apikey':self.key,'sentences':quoted_text,'linenumber':1,'separation':'。'})

urllib.parse.quote()を使ってURLエンコードしてます。 

これで実行すると。 

f:id:arakan_no_boku:20190321144456j:plain

あれあれ・・。

&も空白も関係なく、エラーになりました。

 

じゃあ、何もしない状態で&と空白は処理できないのか?

 

制限事項に書いてあるくらいなので、&や半角スペースを何も処理しないで渡すとエラーになるんだろうか?

これも試してみました。

    quoted_text = inputtext
    r = requests.post(url,{'apikey':self.key,'sentences':quoted_text,'linenumber':1,'separation':'。'})

もう何もしてません。

inputtextをそのまま渡してます。

これだと・・「& & &」みたいな文字列はどうなるんでしょうね。

やってみると。

f:id:arakan_no_boku:20190321150100j:plain

うーーん。

普通に処理されている。

あの制限事項は何なんですかね。

まあ・・とりあえず、そのままでいいやということで。

ではでは。