[PYTHON] Jouez avec Dajare en utilisant l'API COTOHA

introduction

[Plan actuel de l'API Qiita x COTOHA] Analysons le texte avec l'API COTOHA!

J'ai décidé de toucher l'API COTOHA à cause des rumeurs du projet actuel. Pour moi, qui aime dajare, je me suis demandé si je pouvais analyser dajare en utilisant cette API, alors j'ai essayé de le toucher. Je ne m'attends pas du tout à recevoir un cadeau.

Préparation

Tout d'abord, inscrivez-vous pour Developer sur la page API COTOHA. Vous pouvez accéder immédiatement à l'API.

Tant que vous traitez avec Dajare, je pense que ce sera certainement un processus d'essais et d'erreurs. Cependant, le plan for Developer a une limite de 1 000 appels par jour pour chaque API. Si tel est le cas, essayez de créer une classe qui met en cache la même entrée et n'appelle pas inutilement l'API.

COTOHA.py (peut-être en quelque sorte redondant)
import os
import sys
import pathlib
import time
import requests
import json
import hashlib


class COTOHA:
    __BASE_URL = 'https://api.ce-cotoha.com/api/dev/'

    def __init__(self, id, secret, cache_dir='./COTOHA_cache'):
        self.id = id
        self.secret = secret
        self.cache_dir = cache_dir
        self._get_token()

    def _create_cache_path(self, func, key):
        hash = hashlib.md5(key.encode()).hexdigest()
        hashpath = "{}/{}/{}".format(hash[:2], hash[2:4], hash[4:])
        return self.cache_dir + '/' + func + '/' + hashpath

    def _save_cache(self, path, content):
        pathlib.Path(os.path.dirname(path)).mkdir(exist_ok=True, parents=True)
        with open(path, mode="w") as c:
            c.write(content)
        return

    def _load_cache(self, path):
        content = None
        if os.path.exists(path):
            with open(path, mode="r") as c:
                content = c.read()
        return content

    def _get_token(self):
        token_cache = self.cache_dir + '/token'  # format: token expired_time
        cache = self._load_cache(token_cache)
        if cache:
            token, expired_time = cache.split()
            if int(expired_time) > int(time.time()):
                self.token = token
                return

        # get new token
        token_url = 'https://api.ce-cotoha.com/v1/oauth/accesstokens'
        headers = {'content-type': 'application/json'}
        payload = {"grantType": "client_credentials",
                   "clientId": self.id,
                   "clientSecret": self.secret}
        res = requests.post(token_url, headers=headers, data=json.dumps(payload))
        res.raise_for_status()
        res_json = json.loads(res.text)

        self.token = res_json['access_token']
        expired_time = int(time.time()) + int(res_json['expires_in'])
        self._save_cache(token_cache, self.token + ' ' + str(expired_time))
        return

    #Détection d'erreur de reconnaissance vocale(β)
    def detect_misrecognition(self, data):
        func = sys._getframe().f_code.co_name
        cache_path = self._create_cache_path(func, data)
        cache = self._load_cache(cache_path)
        if cache:
            print("[INFO] use '"+func+"' api cache", file=sys.stderr)
            return cache

        api_url = self.__BASE_URL + 'nlp/beta/detect_misrecognition'
        payload = {'sentence': data}
        headers = {'content-type': 'application/json;charset=UTF8',
                   'Authorization': 'Bearer ' + self.token}

        res = requests.post(api_url, headers=headers, data=json.dumps(payload))
        res.raise_for_status()

        self._save_cache(cache_path, res.text)
        return res.text

    #emballer(β)
    def summary(self, data, sent_len=1):
        func = sys._getframe().f_code.co_name
        cache_path = self._create_cache_path(func, data+str(sent_len))
        cache = self._load_cache(cache_path)
        if cache:
            print("[INFO] use '"+func+"' api cache", file=sys.stderr)
            return cache

        api_url = self.__BASE_URL + 'nlp/beta/summary'
        payload = {'document': data, 'sent_len': sent_len}
        headers = {'content-type': 'application/json;charset=UTF8',
                   'Authorization': 'Bearer ' + self.token}

        res = requests.post(api_url, headers=headers, data=json.dumps(payload))
        res.raise_for_status()

        self._save_cache(cache_path, res.text)
        return res.text

    def keyword(self, data, type='default', max_keyword_num=5):
        func = sys._getframe().f_code.co_name
        cache_path = self._create_cache_path(func, data+type+str(max_keyword_num))
        cache = self._load_cache(cache_path)
        if cache:
            print("[INFO] use '"+func+"' api cache", file=sys.stderr)
            return cache

        if type != 'kuzure' and type != 'default':
            print("[ERROR] type must be default or kuzure! :" + type)
            return

        api_url = self.__BASE_URL + 'nlp/v1/keyword'
        payload = {'document': data, 'type': type, 'max_keyword_num': max_keyword_num}
        headers = {'content-type': 'application/json;charset=UTF8',
                   'Authorization': 'Bearer ' + self.token}

        res = requests.post(api_url, headers=headers, data=json.dumps(payload))
        res.raise_for_status()

        self._save_cache(cache_path, res.text)
        return res.text

    def parse(self, data, type='default'):
        func = sys._getframe().f_code.co_name
        cache_path = self._create_cache_path(func, data+type)
        cache = self._load_cache(cache_path)
        if cache:
            print("[INFO] use '"+func+"' api cache", file=sys.stderr)
            return cache

        if type != 'kuzure' and type != 'default':
            print("[ERROR] type must be default or kuzure! :" + type)
            return

        api_url = self.__BASE_URL + 'nlp/v1/parse'
        payload = {'sentence': data, 'type': type}
        headers = {'content-type': 'application/json;charset=UTF8',
                   'Authorization': 'Bearer ' + self.token}

        res = requests.post(api_url, headers=headers, data=json.dumps(payload))
        res.raise_for_status()

        self._save_cache(cache_path, res.text)
        return res.text

    : (Ce qui suit est omis)

Alors, faisons une commande pour que vous puissiez l'appeler comme vous le souhaitez. Je suis nouveau sur python, donc je ne suis pas sûr si argparse est bon, mais pour le moment, c'est rapide. Aucune option n'est jointe.

coto.py
!/usr/bin/env python
import sys
import argparse
from COTOHA import COTOHA

parser = argparse.ArgumentParser()
parser.add_argument('api', choices=['summary', 'keyword', 'parse',
                    'detect_misrecognition'])
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
                    default=sys.stdin)
args = parser.parse_args()

id = ''  #Entrer votre identifiant!
secret = ''  #Mettez dans un secret!
coto = COTOHA(id, secret)

data = args.infile.read()
if args.api == "summary":
    sent_len = 1
    res = coto.summary(data, sent_len)
elif args.api == "keyword":
    type = 'default'
    max_keyword_num = 5
    res = coto.keyword(data, type, max_keyword_num)
elif args.api == "parse":
    type = 'default'
    res = coto.parse(data, type)
elif args.api == "detect_misrecognition":
    res = coto.detect_misrecognition(data)
elif args.api == "sentiment":
    res = coto.sentiment(data)
else:
    print("unexpected api:" + args.api, file=sys.stderr)
    sys.exit(1)

print(res)

D'une manière ou d'une autre, j'essaierai de le frapper avec une herbe d'oreiller (explosion) comme point de départ.

$ cat makurano.txt 
Le printemps est Akebono. Les montagnes, qui finissent par devenir blanches, sont un peu plus claires et les nuages violets flottent.
L'été est la nuit.... (Ce qui suit est omis, jusqu'à l'hiver.)
$ [hoshino@localhost py_scrape]$ ./coto.py summary makurano.txt 
{"result":"À midi, si vous vous détendez chaudement, le feu dans le brasier aura tendance à être de la cendre blanche.","status":0}
$ ./coto.py keyword makurano.txt 
{
  "result" : [ {
    "form" : "Voir",
    "score" : 21.3012
  }, {
    "form" : "Itotsuki",
    "score" : 20.0
  }, {
    "form" : "Feu",
    "score" : 17.12786
  }, {
    "form" : "Où dormir",
    "score" : 11.7492
  }, {
    "form" : "Le coucher du soleil",
    "score" : 11.4835
  } ],
  "status" : 0,
  "message" : ""
}

Ouais, jeter soudainement une phrase ancienne a une mauvaise personnalité. Je ne connais pas la validité du résultat w Allons-y un peu plus clairement.

$ echo "Je suis sukiyaki aujourd'hui, j'ai hâte d'y être." | ./coto.py sentiment
{"result":{"sentiment":"Positive","score":0.6113335958534332,"emotional_phrase":[{"form":"J'ai hâte d'y être","emotion":"P"}]},"status":0,"message":"OK"}

$ echo "Je suis sukiyaki aujourd'hui, je ne veux pas en manger." | ./coto.py sentiment
{"result":{"sentiment":"Neutral","score":0.2837920794741674,"emotional_phrase":[]},"status":0,"message":"OK"}

$ echo "C'est du sukiyaki aujourd'hui, c'est dur à manger." | ./coto.py sentiment
{"result":{"sentiment":"Negative","score":0.7608419653662558,"emotional_phrase":[{"form":"Épicé","emotion":"N"}]},"status":0,"message":"OK"}

$ echo "Aujourd'hui, c'est sukiyaki. Pourquoi mangez-vous ce genre de nourriture?" | ./coto.py sentiment
{"result":{"sentiment":"Neutral","score":0.3482213983910368,"emotional_phrase":[]},"status":0,"message":"OK"}

$ echo "Aujourd'hui, c'est sukiyaki. Bien." | ./coto.py sentiment
{"result":{"sentiment":"Positive","score":0.0976613052132079,"emotional_phrase":[{"form":"Yosha","emotion":"Réjouir"}]},"status":0,"message":"OK"}

Hmmm, l'analyse des émotions ne semble pas être prise sans des expressions faciles à comprendre. J'ai beaucoup de choses à penser.

Avec ce sentiment, vous pouvez désormais appeler l'API librement telle quelle. J'ai pensé après l'avoir fait, mais si je fais 1000 appels, je n'ai pas besoin d'argent (hey)

Gratter pour apporter dajare

Au fait, je vais apporter un dajare ludique du putain de blog que j'avais l'habitude d'écrire. Le grattage utilise la belle soupe familière (?) Dans "Laisser Python faire les choses ennuyeuses". Vous pouvez comprendre cet article en 10 minutes.

Ceci est également mis en cache car il est médiocre. Argent préféré. Je ferai de mon mieux pour regarder la source html et extraire la partie de l'article. Affiche le contenu d'un article analysé par get_text.

scrape.py


if not os.path.exists("hijili/top"):
    res = requests.get("https://ameblo.jp/hijili/")
    res.raise_for_status()
    os.makedirs("./hijili/")
    with open("./hijili/top", mode="w") as f:
        f.write(res.text)

with open("hijili/top", mode="r") as f:
    top_soup = bs4.BeautifulSoup(f, 'html.parser')

top_soup.find('entryBody')
bodies = [n.get_text() for n in top_soup.select('div#entryBody')]

print(bodies[1])
$ python scrape.py
Quand je parlais de Katana, je pensais que c'était bien de pouvoir revenir "Je l'ai acheté il y a longtemps" (1 feuillet). Je pensais avoir gagné, mais je ne l'ai pas dit. (2 feuillets) Ce n'est pas grave de dire que je l'ai acheté. (3 feuillets qui plaisent sérieusement)

Il y a beaucoup de merde comme celle-ci, vous pouvez donc l'analyser autant que vous le souhaitez! J'ai choisi quelque chose qui semble relativement facile à comprendre (je ne sais pas comment la partie "sérieuse" est impliquée ...). Cependant, la partie "(N slip)" qui est la promesse de ce blog semble interférer avec l'analyse, je vais donc la couper.

# bodies = [n.get_text() for n in top_soup.select('div#entryBody')]
bodies = [re.sub('([^)]*Glissement)', '', n.get_text()) for n in top_soup.select('div#entryBody')]
$ python scrape.py
Quand je parlais de Katana, je pensais que c'était bien de pouvoir lui rendre: «Je l'ai acheté il y a longtemps». Je pensais avoir gagné, mais je ne l'ai pas dit. Même si je dis que je l'ai acheté, ce n'est pas grave.

D'accord, cela devrait rendre COTOHA facile à lire.

Jouez avec Dajare avec l'API COTOHA

Pour le moment, passons-le via l'API Gashigashi avec la classe COTOHA. En guise d'attente, j'aimerais avoir un indice qui jugera Dajare comme Dajare ...

#emballer
{"result":"Quand je parlais de Katana, je pensais que c'était bien de pouvoir lui rendre: «Je l'ai acheté il y a longtemps».","status":0}

#Extraction de mots-clés
{
  "result" : [ {
    "form" : "Sérieux",
    "score" : 22.027
  }, {
    "form" : "bouche",
    "score" : 8.07787
  }, {
    "form" : "Parler",
    "score" : 7.23882
  } ],
  "status" : 0,
  "message" : ""
}

#Analyse des émotions
{"result":{"sentiment":"Positive","score":0.06420815417815495,"emotional_phrase":[{"form":"C'était bien","emotion":"P"},{"form":"J'ai gagné","emotion":"P"},{"form":"Pas sérieux","emotion":"PN"}]},"status":0,"message":"OK"}

#Estimation des attributs utilisateur(β)
{
  "result" : {
    "age" : "20-29 ans",
    "civilstatus" : "marié",
    "hobby" : [ "CAMERA", "COOKING", "INTERNET", "MUSIC", "SHOPPING" ],
    "location" : "Kanto",
    "moving" : [ "WALKING" ],
    "occupation" : "employé"
  },
  "status" : 0,
  "message" : "OK"
}

Hmmm, quelle est la différence entre ce qui est formaté et renvoyé et ce qui ne l'est pas ... Eh bien, ça va car il est traité par python.

L'API de synthèse ne résume-t-elle pas, elle renvoie simplement ce qui semble important dans une longue phrase?

Extraction de mots-clés, je voulais que vous extrayiez Katana pour cette phrase, mais il semble que vous ne puissiez pas trouver le mot-clé Dajare ...

C'était utile même si j'analysais mes émotions! La personne elle-même n'écrit que négligemment, donc c'est toujours positif.

Estimation des attributs utilisateur, hmm, je pense que des spécifications qui semblent stupides sortent, mais Sman, je suis plus vieux et célibataire.

Avec ce sentiment, je ne pouvais pas confirmer l'efficacité de Dajare ... (c'est une évidence).

Cependant, il y a de l'espoir pour la détection des erreurs de reconnaissance vocale! Un mot qui ressemble à «sérieux» sort!

#Détection d'erreur de reconnaissance vocale(β)
{"result":{"score":0.7298996038673021,"candidates":[{"begin_pos":76,"end_pos":78,"form":"Sérieux","detect_score":0.7298996038673021,"correction":[{"form":"tester","correct_score":0.7367702251579388},{"form":"Littérature","correct_score":0.7148903980845676},{"form":"Nerveux","correct_score":0.6831886720211694},{"form":"Shinken","correct_score":0.6443806737641236},{"form":"Nintendo","correct_score":0.6403453079473043}]}]},"status":0,"message":"OK"}

Prenez le test au sérieux! (1 feuillet) Concentrez vos nerfs sur le test! !! (2 feuillets) Ninken a sérieusement enquêté sur quelque chose (non, qu'est-ce que Ninken vraiment!?)! !! !! (3 feuillets)

Il semble que COTOHA pense aux candidats pour Dajare! Je vois, devrais-je l'utiliser comme ça! (Faux

Je vais découvrir Dajare correctement

Donc, j'ai eu des résultats de type dajare, mais il semble difficile de trouver du dajare. Cependant, j'ai senti qu'il serait possible d'utiliser correctement les résultats de l'API d'analyse syntaxique.

Le résultat de l'API d'analyse syntaxique est long, je vais donc l'omettre ... Vous pouvez l'essayer rapidement sur la page Démo.

Ce que je pensais, c'est que si je trouvais la partie la plus longue avec la même voyelle entre les clauses séparées par cette analyse syntaxique, ne serait-ce pas fou? à propos de ça.

Je pensais l'essayer pour le moment, mais que diriez-vous de «trouver la partie la plus longue avec la même voyelle»? Je me demande s'il sera possible de le convertir en Romaji et d'extraire [aiueo] et de le gâcher ... Recherche ... Je vois, pykakashi est dans l'implémentation python de KAKASI. Eh bien, essayons la conversion!

Essayez d'utiliser pykakashi
kakasi = kakasi()  # Generate kakasi instance
kakasi.setMode("H", "a")  # Hiragana to ascii
kakasi.setMode("K", "a")  # Katakana to ascii
kakasi.setMode("J", "a")  # Japanese(kanji) to ascii
kakasi.setMode("r", "Kunrei")   #Cérémonie d'instruction
conv = kakasi.getConverter()

res = coto.parse(data, 'default')
j = json.loads(res)

org_sentence = []
ascii_sentence = []
tmp_org_sentence = []
tmp_ascii_sentence = []
org_chunk = ""
kana_chunk = ""
for chunk_info in j['result']:
    for token in chunk_info['tokens']:
        is_end = False
        if token['form'] == '。' or token['form'] == '.' or token['form'] == '!':
            is_end = True
            break
        else:
            # chunk += conv.do(token['kana'])
            org_chunk += token['form']
            kana_chunk += token['kana']

    tmp_org_sentence.append(org_chunk)
    tmp_ascii_sentence.append(conv.do(kana_chunk))
    org_chunk = ""
    kana_chunk = ""
    if is_end:
        org_sentence.append(tmp_org_sentence)
        ascii_sentence.append(tmp_ascii_sentence)
        tmp_org_sentence = []
        tmp_ascii_sentence = []

print("org")
print(*org_sentence, sep='\n')
print("ascii")
print(*ascii_sentence, sep='\n')

résultat.

org
['Katana', 'Parler', 'Était', 'quelquefois', '"Vieux temps', 'Je l'ai acheté. "', 'Pressé', 'J'ai pu revenir', 'C'était bon', 'pensée']
['J'ai gagné', 'pensée,', 'Dans la bouche', 'Je ne l'ai pas éteint']
['je l'ai acheté', 'Après tout', 'Pas sérieux']
ascii
['katanano', 'hanasio', 'siteita', 'tokini', 'mukasi', 'kattanaato', 'tossani', 'kaesetanoha', 'yokattato', 'omoimasita']
['kattanato', 'omoimasita', 'kutiniha', 'dasanakattakedo']
['kattanaato', 'ittemo', 'sinkendehaarimasen']

Oh, j'ai pu le démonter d'une belle manière. Alors je vais essayer ça.

for i, osen in enumerate(org_sentence):
    c = find_dajare_candidate(ascii_sentence[i], osen)  #Je ne peux pas tellement le montrer, donc si ça fait du bien à l'avenir ...
    if not c:
        continue
    dump_candidate(c)
    print('----')

Dans find_dajare_candidate, il est supposé qu'une phrase doit contenir dajare pour le moment, les voyelles de chaque phrase de la phrase sont comparées et la partie avec le plus grand nombre de correspondances est renvoyée en tant que données candidates.

Katana
Je l'ai acheté. "
vowel: aaao  score:4
----
J'ai gagné
Je ne l'ai pas éteint
vowel: aaao  score:4
----
je l'ai acheté
Pas sérieux
vowel: aaa  score:3
----

Eh bien, le premier est correct, mais le reste est subtil. Je veux dire, [«J'ai gagné», «je pensais», «dans ma bouche», «je ne l'ai pas dit»] Dans la deuxième phrase, cela n'inclut pas le dajare, cela dépend juste de la première phrase, "Katana".

Ensuite, si vous essayez de mettre un mot-clé de comparaison pendant un moment pour pouvoir effectuer une recherche en spécifiant "katana" ...

c = find_dajare_candidate(ascii_sentence[1], org_sentence[1], "katana")
dump_candidate(c)
print('----')
c = find_dajare_candidate(ascii_sentence[2], org_sentence[2], "katana")
dump_candidate(c)
J'ai gagné
vowel: aaa  score:3
----
je l'ai acheté
vowel: aaa  score:3

Je peux vous guider dans une certaine mesure! Si vous pouvez comparer correctement la similitude des consonnes avec cela, il semble que la précision sera améliorée, d'accord, je ferai de mon mieux! !! !! !!








cette…?





** COTOHA n'est plus d'actualité! C'est à peu près hors de propos au cœur! !! ** (saveur Haraichi)



Fin Production / rédaction          ━━━━━           ⓃⒽⓀ

en conclusion

Alors, comment était l'article "Utiliser l'API COTOHA ~~ pour jouer avec Dajare ~~"? Il semblait impossible pour COTOHA de découvrir Dajare, mais il semblait qu'il pouvait obtenir des indices pour Dajare et l'utiliser comme point de départ pour l'analyse.

J'espère que quelqu'un deviendra un enfant qui utilise COTOHA. (1 feuillet)

Mon engagement envers un tel dajare est décrit dans Mach New Book Like Fucking. Veuillez voir si vous êtes trop libre pour mourir.

Je veux dire, je suis intéressé par le projet actuel, et j'en suis reconnaissant car il est devenu beaucoup plus amusant. Je vais continuer cette analyse fulgurante pendant un moment (même si j'en ai marre).

Recommended Posts