5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Yahoo!ニュースの見出し生成にCOTOHA APIだけを使ってチャレンジ

Posted at

#はじめに
COTOHA APIがQiitaとコラボキャンペーンをやっている。
もうすぐFF7のリメイク出るしPS4が欲しい.. (-p-)

完全に不純な動機ですが、COTOHA APIを使って自然言語処理をやってみました。
本日が投稿締め切りなのでかなりギリギリですが、何とか間に合いました...

#やったこと
COTOHAが提供するAPIだけを使って、ニュース記事の要約にチャレンジしてみました。
お題はYahoo!ニュースの見出し生成です。

#Yahoo!ニュースの見出し
みなさんご存知Yahoo!ニュースですが、各記事には見出しが付けられています。
例えば以下のような感じです。

スクリーンショット 2020-03-10 23.54.59.png

普段何気なく見ているこの見出しですが、実は様々なルールの中で作られており奥が深いのです。

まず、限られたスペースで、シンプルに齟齬なく伝えるようにするために
文字数は最大13文字(正確には半角スペース込みの13.5文字)に制限されています。

また、見出しには場所情報が含まれています。
事件や事故の場合、発生場所によってニュースの重要性やユーザーの関心度が大きく変わるためです。

そして、見出しに使う言葉には基本的に記事中の言葉が使われています。
というのも、記事は各メディアに配信してもらっているため、字数に収まらない場合を除き、
記事の内容をねじ曲げることが無いようにそうしているそうです。

記事中の言葉を使って見出しを作るのであれば、COTOHA APIを使って
ある程度出来るのではないかと思いました。

他にもルールはありますが、今回取り上げたルールをまとめると以下のようになります。

  • 【ルール1】 見出しの文字数は最大でも13文字以内
  • 【ルール2】 見出しには場所情報を含める
  • 【ルール3】 見出しには記事中の言葉を使う

※ルールについては以下のページにまとめられているので、興味がある方は是非みてください。

【参考】Yahoo!ニュース トピックス「13文字見出し」の極意
https://news.yahoo.co.jp/newshack/inside/yahoonews_topics_heading.html

#COTOHA API
NTTコミュニケーションズが提供する自然言語処理・音声認識のAPIです。
構文解析や音声認識など14の自然言語処理・音声処理APIが提供されています。
https://api.ce-cotoha.com/contents/index.html

今回は、COTOHA APIのDevelopers版を使いました。
Enterprise版と比べて一部制約はありますが、無料で利用することが出来ます。

#今回対象にした記事
今回対象にしたのは、ビル・ゲイツさんがMSを退任されたという以下の記事です。
https://news.yahoo.co.jp/pickup/6354056

付けられていた見出しはこちらです。

ビル・ゲイツ氏 MS取締役退任

うむ。確かに完結で分かりやすい。

#ステップ1:まずは要約APIだけでチャレンジ
COTOHA APIでは要約APIが提供されています。
まだベータ版ではありますが、こちらを使うことにより文中で重要と思われる文章を抽出することが出来ます。

まずは、このAPIを使い1文を抽出してみることにしました。

{
  "result": "ゲイツ氏は2008年に経営の一線から退き、14年には会長を退任したが、取締役会には残っていた。",
  "status": 0
}

無事抽出できましたが、このままだと13文字を明らかに超えているので、短縮しなければなりません。
どう短縮しようか悩みましたが、重要度が高いキーワードだけを残すというやり方で出来ないか進めてみることにしました。

#ステップ2:キーワード抽出APIを使って重要語抽出
以前、Qiitaの記事でtermextractを使うことで、重要度が高いキーワード抽出が出来ると書きました。

【参考】Qiitaタグ自動ジェネレータ
https://qiita.com/fukumasa/items/7f6f69d4f6336aff3d90

COTOHA APIでもキーワード抽出APIが提供されており、
テキストに含まれる特徴的なフレーズ・単語をキーワードとして抽出することが出来ます。

先ほど抽出した1文を対象にキーワードを抽出してみます。

{
  "result": [
    {
      "form": "会長",
      "score": 14.48722
    },
    {
      "form": "一線",
      "score": 11.3583
    },
    {
      "form": "退任",
      "score": 11.2471
    },
    {
      "form": "取締役会",
      "score": 10.0
    }
  ],
  "status": 0,
  "message": ""
}

この時点で既に怪しい感じになってきました・・・。
「誰が」という肝心な情報(ゲイツ氏)が抽出できていません。
まあ、、、とりあえず続けてみます。

#ステップ3:固有表現抽出APIを使って場所情報を抽出
冒頭のルールでも書きましたが、見出しには場所情報を含める必要があります。
場所情報を取得するのに便利なAPIがCOTOHAでは提供されています。固有表現抽出APIです。
このAPIを使うことで、人名や地名などの固有表現が取得できます。

先ほど抽出した1文で試してみましたが、場所情報は含まれていませんでした。

もし含まれていた場合は、めちゃ安直ですが**「抽出した場所情報」に「で」**をつけて、
要約文の冒頭に含めることにしました。

#ステップ4:構文解析APIを使ってキーワードに助詞を付与
抽出出来たこれらのキーワードをただ並べるだけでは、見出し(文章)を生成するのは難しそうです。
キーワードをもとに文章を自動生成するなんて高度なこと出来ませんし、、結構悩みました。

COTOHA APIだけを使うという制約を課しているため、改めてAPI一覧を眺めていたところ1つ閃きました。
構文解析APIを使って、抽出した各キーワードに「が」や「を」などの助詞を付与して各キーワードをつなげるという方法です。

このAPIを使うことで、テキストが文節・形態素に分解され、文節間の係り受け関係や
形態素間の係り受け関係、品詞情報などの意味情報などが付与されます。

これで、抽出したキーワードと助詞の関係にあるもの?(どう表現したらいいかよく分からない...)を抽出できそうです。例えば、「空気が美味い」の場合、「空気」というキーワードの助詞として「が」を抽出するという感じです。

このAPIを使って、先ほどのキーワードに助詞を付与してみます。

返却されたレスポンス(長いので折りたたみ)
{
  "result": [
    {
      "chunk_info": {
        "id": 0,
        "head": 7,
        "dep": "D",
        "chunk_head": 1,
        "chunk_func": 2,
        "links": []
      },
      "tokens": [
        {
          "id": 0,
          "form": "ゲイツ",
          "kana": "ゲイツ",
          "lemma": "ゲイツ",
          "pos": "名詞",
          "features": [
            "固有",
            "姓"
          ],
          "attributes": {}
        },
        {
          "id": 1,
          "form": "氏",
          "kana": "シ",
          "lemma": "氏",
          "pos": "名詞接尾辞",
          "features": [
            "名詞"
          ],
          "dependency_labels": [
            {
              "token_id": 0,
              "label": "name"
            },
            {
              "token_id": 2,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 2,
          "form": "は",
          "kana": "ハ",
          "lemma": "は",
          "pos": "連用助詞",
          "features": [],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 1,
        "head": 4,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 3,
          "form": "2008年",
          "kana": "ニセンハチネン",
          "lemma": "2008年",
          "pos": "名詞",
          "features": [
            "日時"
          ],
          "dependency_labels": [
            {
              "token_id": 4,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 4,
          "form": "に",
          "kana": "ニ",
          "lemma": "に",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 2,
        "head": 3,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 5,
          "form": "経営",
          "kana": "ケイエイ",
          "lemma": "経営",
          "pos": "名詞",
          "features": [
            "動作"
          ],
          "dependency_labels": [
            {
              "token_id": 6,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 6,
          "form": "の",
          "kana": "ノ",
          "lemma": "の",
          "pos": "格助詞",
          "features": [
            "連体"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 3,
        "head": 4,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": [
          {
            "link": 2,
            "label": "adjectivals"
          }
        ]
      },
      "tokens": [
        {
          "id": 7,
          "form": "一線",
          "kana": "イッセン",
          "lemma": "一線",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 5,
              "label": "nmod"
            },
            {
              "token_id": 8,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 8,
          "form": "から",
          "kana": "カラ",
          "lemma": "から",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 4,
        "head": 7,
        "dep": "P",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": [
          {
            "link": 1,
            "label": "goal"
          },
          {
            "link": 3,
            "label": "object"
          }
        ],
        "predicate": []
      },
      "tokens": [
        {
          "id": 9,
          "form": "退",
          "kana": "シリゾ",
          "lemma": "退く",
          "pos": "動詞語幹",
          "features": [
            "K"
          ],
          "dependency_labels": [
            {
              "token_id": 3,
              "label": "nmod"
            },
            {
              "token_id": 7,
              "label": "dobj"
            },
            {
              "token_id": 10,
              "label": "aux"
            },
            {
              "token_id": 11,
              "label": "punct"
            }
          ],
          "attributes": {}
        },
        {
          "id": 10,
          "form": "き",
          "kana": "キ",
          "lemma": "き",
          "pos": "動詞接尾辞",
          "features": [
            "連用"
          ],
          "attributes": {}
        },
        {
          "id": 11,
          "form": "、",
          "kana": "",
          "lemma": "、",
          "pos": "読点",
          "features": [],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 5,
        "head": 7,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 12,
          "form": "14年",
          "kana": "ジュウヨネン",
          "lemma": "14年",
          "pos": "名詞",
          "features": [
            "日時"
          ],
          "dependency_labels": [
            {
              "token_id": 13,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 13,
          "form": "には",
          "kana": "ニハ",
          "lemma": "には",
          "pos": "連用助詞",
          "features": [],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 6,
        "head": 7,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 14,
          "form": "会長",
          "kana": "カイチョウ",
          "lemma": "会長",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 15,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 15,
          "form": "を",
          "kana": "ヲ",
          "lemma": "を",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 7,
        "head": 9,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 3,
        "links": [
          {
            "link": 0,
            "label": "agent"
          },
          {
            "link": 4,
            "label": "manner"
          },
          {
            "link": 5,
            "label": "time"
          },
          {
            "link": 6,
            "label": "agent"
          }
        ],
        "predicate": [
          "past"
        ]
      },
      "tokens": [
        {
          "id": 16,
          "form": "退任",
          "kana": "タイニン",
          "lemma": "退任",
          "pos": "名詞",
          "features": [
            "動作"
          ],
          "dependency_labels": [
            {
              "token_id": 1,
              "label": "nsubj"
            },
            {
              "token_id": 9,
              "label": "advcl"
            },
            {
              "token_id": 12,
              "label": "nmod"
            },
            {
              "token_id": 14,
              "label": "nsubj"
            },
            {
              "token_id": 17,
              "label": "aux"
            },
            {
              "token_id": 18,
              "label": "aux"
            },
            {
              "token_id": 19,
              "label": "mark"
            },
            {
              "token_id": 20,
              "label": "punct"
            }
          ],
          "attributes": {}
        },
        {
          "id": 17,
          "form": "し",
          "kana": "シ",
          "lemma": "し",
          "pos": "動詞活用語尾",
          "features": [],
          "attributes": {}
        },
        {
          "id": 18,
          "form": "た",
          "kana": "タ",
          "lemma": "た",
          "pos": "動詞接尾辞",
          "features": [
            "接続"
          ],
          "attributes": {}
        },
        {
          "id": 19,
          "form": "が",
          "kana": "ガ",
          "lemma": "が",
          "pos": "接続接尾辞",
          "features": [
            "連用"
          ],
          "attributes": {}
        },
        {
          "id": 20,
          "form": "、",
          "kana": "",
          "lemma": "、",
          "pos": "読点",
          "features": [],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 8,
        "head": 9,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 21,
          "form": "取締役会",
          "kana": "トリシマリヤクカイ",
          "lemma": "取締役会",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 22,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 22,
          "form": "には",
          "kana": "ニハ",
          "lemma": "には",
          "pos": "連用助詞",
          "features": [],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 9,
        "head": -1,
        "dep": "O",
        "chunk_head": 0,
        "chunk_func": 4,
        "links": [
          {
            "link": 7,
            "label": "manner"
          },
          {
            "link": 8,
            "label": "place"
          }
        ],
        "predicate": [
          "past",
          "past"
        ]
      },
      "tokens": [
        {
          "id": 23,
          "form": "残",
          "kana": "ノコ",
          "lemma": "残る",
          "pos": "動詞語幹",
          "features": [
            "R"
          ],
          "dependency_labels": [
            {
              "token_id": 16,
              "label": "advcl"
            },
            {
              "token_id": 21,
              "label": "nmod"
            },
            {
              "token_id": 24,
              "label": "aux"
            },
            {
              "token_id": 25,
              "label": "aux"
            },
            {
              "token_id": 26,
              "label": "aux"
            },
            {
              "token_id": 27,
              "label": "aux"
            },
            {
              "token_id": 28,
              "label": "punct"
            }
          ],
          "attributes": {}
        },
        {
          "id": 24,
          "form": "っ",
          "kana": "ッ",
          "lemma": "っ",
          "pos": "動詞活用語尾",
          "features": [],
          "attributes": {}
        },
        {
          "id": 25,
          "form": "て",
          "kana": "テ",
          "lemma": "て",
          "pos": "動詞接尾辞",
          "features": [
            "接続",
            "連用"
          ],
          "attributes": {}
        },
        {
          "id": 26,
          "form": "い",
          "kana": "イ",
          "lemma": "いる",
          "pos": "動詞語幹",
          "features": [
            "A",
            "Lて連用"
          ],
          "attributes": {}
        },
        {
          "id": 27,
          "form": "た",
          "kana": "タ",
          "lemma": "た",
          "pos": "動詞接尾辞",
          "features": [
            "終止"
          ],
          "attributes": {}
        },
        {
          "id": 28,
          "form": "。",
          "kana": "",
          "lemma": "。",
          "pos": "句点",
          "features": [],
          "attributes": {}
        }
      ]
    }
  ],
  "status": 0,
  "message": ""
}
['会長を', '一線から', '退任', '取締役会には']

#ステップ5:いざ見出しを生成する!
先ほど助詞を付与した各キーワードを組み合わせて、13文字以下の文章にしてみます。
ステップ4でほぼバレてますが、こんな結果になりました。

会長を一線から退任

「え?誰が退任したの?」って興味をそそるような見出しになっているような気がしないでもないですが、会長を辞めたのではなく取締役を辞めたので誤った情報を伝えてしまいそうです。

ただ、ステップ2でも書いた通り、「誰が」と言う情報や「マイクロソフト」「MS」といった会社名も無く微妙な気もするので、今回生成した見出しがどれ程いい感じなのかを客観的に調べてみることにしました。

#ステップ6:類似度算出APIを使って生成した見出しの完成度を確認
生成した見出しの完成度についても、COTOHAのAPIを使って確認することができます。
類似度算出APIです。
このAPIを使うことで、2つの文における意味的な類似度を算出することが出来ます。
類似度は0から1の定義域で出力され、1に近づくほどテキスト間の類似性が大きいことを示します。

記事につけられていた見出しビル・ゲイツ氏 MS取締役退任と、
生成した見出し会長を一線から退任の類似度を算出してみました。

{
  "result": {
    "score": 0.9716939
  },
  "status": 0,
  "message": "OK"
}

お〜、0.97って結構高くないですか・・・!?(困惑
COTOHAさんがそう言ってくれるなら。。

#おまけ
参考までに他の記事でもやってみました。

###コンビニ「レンチン」で革命
全部で4ページの記事ですが、とりあえず1ページ目だけでやってみました。
https://news.yahoo.co.jp/pickup/6353834

●抽出した1文
いま、コンビニ業界で進むレンジでチン革命を象徴する商品として注目されている。

●抽出したキーワード
['象徴', '注目', 'レンジ', 'コンビニ業界', 'チン革命']

●生成した見出し

象徴注目レンジで  (類似度:0.45899978)

見出し見ても何のこっちゃですね・・・類似度もめちゃ低い。
なんせ抽出したキーワードをスコアの高い順にくっつけて文章にしているだけなので、
こんな感じになっているんだと思います。
レンジでチンをレンチンと略すの初めて知ったかも。というか、チン革命ってなんぞや・・・。

###ゲームは1日60分 案に賛成8割
色々物議を醸している香川県のゲーム規制に関する記事です。
https://news.yahoo.co.jp/pickup/6353894

●抽出した1文
香川県議会が4月の施行を目指している「ゲーム依存」の対策条例。

●抽出したキーワード
['対策条例', '香川県議会', '施行', 'ゲーム依存']

●生成した見出し

対策条例香川県議会施行を  (類似度:0.2842004)

見出しを見れば香川県の例のゲームのやつかと分からんでも無いですが、この記事で伝えたいことは恐らく8割も賛成者がいるということでしょう。類似度もめちゃ低い。
ただ、抽出した1文および生成された見出しには数値情報が含まれていませんでした。
あと、記事中には84%という具体的な数値が含まれていますが、見出しでは8割と分かりやすい表現に変換されています。細かな数値を言われるよりも、ざっくりと言われた方が感覚的に把握しやすいですね。この辺りは人間ならではの技でしょうか。

###東京で降雪 都心はみぞれ
昨日の記事です。東京で雪が降ったそうですね。まだ寒い日が続きますね・・・。
https://news.yahoo.co.jp/pickup/6354091

●抽出した1文
昨日の正午より10℃以上低く東京都心では、14日0時過ぎに最高気温12.3℃を観測した後、どんどん気温が下がり、14時現在は2.5℃となっています。

●抽出したキーワード
['観測', '気温', '12.3°c', '2.5°c', '10°c以上']

●抽出した場所情報
['東京都心']

●生成した見出し

東京都心で観測気温  (類似度:0.99335754)

場所情報が含まれているケースですが、東京都心で4文字も消費してしまい、出来た見出しも情報量があまり無いものになっています。抽出したキーワードも数値情報が多すぎる気もします。
でも、類似度が0.99とめちゃ高い・・・。

#まとめ
今回生成できた見出しが大成功かと言われれば若干微妙な気もしますが、、やってて楽しかったです。
そもそもですが、要約について調べたところ、ざっくり以下のような分類があるそうです。

  • 抽出型
    • 対象の文章の中から重要と思われる文を抽出して要約を作成する
  • 抽象型
    • 元の文章には含まれていない単語も使って文章の意味を汲み取ったうえで適切な要約を作成する

今回使ったCOTOHAが提供している要約APIは前者の抽出型です。

ただ、Yahoo!ニュースでやっているように、色んなルールや制約のもとで要約を作成しようとすると、抽出型だけでは難しいので、他のサービスと組み合わせや後者の抽象型の要約サービスを使うといったことが必要になりそうだと感じました。

また、字数を削減するために、国名などの場合はある程度ルール化されているので省略しやすそうですが、レンジでチンをレンチンと略したり、84%を8割と分かりやすく表現したりするのは、自然言語処理技術を使ってもまだハードルが高そうな気がします。

Yahoo!ニュースの見出し生成(巧みの技)がAIにとって変わられる日はまだまだ当分来ないんだろうなと感じました。

#さいごに
自然言語処理、面白いので個人的に大好きです。
ただ、仕事でバリバリ使う機会があまりないので、引き続きプライベートで楽しみたいと思います。
あとPS4ください。

#参考サイト
要約については以下のQiita記事がすごく参考になりますよ。

#【参考】ソースコード

突っ込みどころ多いかもしれませんが...興味ある人は見てね
import requests
import pprint
import json
import re
from bs4 import BeautifulSoup

base_url = 'https://api.ce-cotoha.com/api/dev/nlp/'


'''
COTOHA APIのアクセストークンを取得
'''
def get_access_token():
    url = 'https://api.ce-cotoha.com/v1/oauth/accesstokens'

    req_data = {
      'grantType' : 'client_credentials',
      'clientId' : 'クライアントID',
      'clientSecret' : 'クライアントシークレット'
    }

    headers = {
        'Content-Type' : 'application/json'
    }

    response = requests.post(url, json.dumps(req_data), headers=headers)
    token = response.json()['access_token']
    
    return token


'''
要約APIを呼び出す
'''
def get_summary(token, document) :
    url = base_url + '/beta/summary'

    req_data = {
        'document' : document,
        'sent_len' : '1'
    }

    headers = {
        'Content-Type' : 'application/json;charset=UTF-8',
        'Authorization' : 'Bearer {}'.format(token)
    }

    response = requests.post(url, json.dumps(req_data), headers=headers)
    summary = response.json()['result']
    
    return summary


'''
キーワード抽出APIを呼び出す
'''
def get_keywords(token, document):
    url = base_url + '/v1/keyword'

    req_data = {
        'document' : document,
        'type' : 'default',
        'do_segment' : True
    }

    headers = {
        'Content-Type' : 'application/json;charset=UTF-8',
        'Authorization' : 'Bearer {}'.format(token)
    }

    response = requests.post(url, json.dumps(req_data), headers=headers)
    keywords = [item.get('form') for item in response.json()['result']]
    
    return keywords


'''
固有表現抽出APIを呼び出し、場所に関する情報を取得
'''
def get_ne_loc(token,sentence):
    url = base_url + '/v1/ne'

    req_data = {
        'sentence' : sentence
    }

    headers = {
        'Content-Type' : 'application/json;charset=UTF-8',
        'Authorization' : 'Bearer {}'.format(token)
    }
    
    response = requests.post(url, json.dumps(req_data), headers=headers)
    ne = response.json()['result']
    
    ne_loc = []
    for item in ne:
        if item['class'] == 'LOC':
            ne_loc.append(item['form'])
    
    #単語だけだと重複が発生するケースがある
    if ne_loc:
        ne_loc = list(set(ne_loc))
        
    return ne_loc

    
'''
構文解析APIを呼び出す
'''
def parse_doc(token, sentence) :
    url = base_url + '/v1/parse'

    req_data = {
        'sentence':sentence
    }

    headers = {
        'Content-Type' : 'application/json;charset=UTF-8',
        'Authorization' : 'Bearer {}'.format(token)
    }
    
    response = requests.post(url, json.dumps(req_data), headers=headers)
    parsed_result = response.json()['result']
    
    tokens = []
    for tokens_ary in parsed_result:
        for token in tokens_ary['tokens']:
            if token:
                tokens.append(token)
        
    return tokens


'''
類似度算出APIを呼び出す
'''
def get_similarity(token, doc1, doc2):
    url = base_url + '/v1/similarity'

    req_data = {
        's1' : doc1,
        's2' : doc2,
        'type' : 'kuzure'
    }

    headers = {
        'Content-Type' : 'application/json;charset=UTF-8',
        'Authorization' : 'Bearer {}'.format(token)
    }
    
    response = requests.post(url, json.dumps(req_data), headers=headers)
    similarity = response.json()['result']
        
    return similarity       


'''
Yahoo!ニュースの記事URLから内容を抽出する
(単一ページのみに対応、複数ページや特定の記事フォーマットには対応していない...)
'''
def get_contents(url):
    top_page = requests.get(url) 
    soup = BeautifulSoup(top_page.text, 'lxml')
    article_url = soup.find('div',class_=re.compile('pickupMain_articleInfo')).find('a').get('href')
    article_page = requests.get(article_url) 
    soup = BeautifulSoup(article_page.text, "lxml")
    for tag in soup.find_all('p',{'class':'photoOffer'}):
        tag.decompose()
    for tag in soup.find_all('a'):
        tag.decompose()
    contents =  re.sub('\n|\u3000','',soup.find('div',class_=re.compile('articleMain')).getText());
    
    return contents


'''
Yahoo!ニュースの記事URLからタイトルを抽出する
(これが正解になる)
'''
def get_title(url):
    top_page = requests.get(url) 
    soup = BeautifulSoup(top_page.text, "lxml")
    title = soup.find("title").getText().split(' - ')[0]
    
    return title


'''
Yahoo!ニュース記事のトピックを生成する
'''
def create_news_topic(token, contents):
    #記事要約を実施
    summary = get_summary(token, contents)
    print(summary)
    print("------------")
    
    #要約文が13文字以下ならトピックとして返す
    if len(summary) <= 13:
        return summary[:-1]

    #要約文からキーワードおよび地名を抽出
    keywords = get_keywords(token, summary)
    print(keywords)
    print("------------")
    ne_loc = get_ne_loc(token, summary)
    print(ne_loc)
    print("------------") 

    topic = ''
    #場所情報があれば見出しに追加
    #複数あってもとりあえず1個目だけ
    if ne_loc:
        topic += ne_loc[0] + ''
        #キーワードにも含まれている場合は取り除く
        if ne_loc[0] in keywords:
            keywords.remove(ne_loc[0])

    #要約文を構文解析
    tokens = parse_doc(token, summary)

    #キーワードの助詞を取得しつつ要約を作成
    for keyword in keywords:
        for token in tokens:
            if token['form'] == keyword:
                print(token)
                for dependency_label in token['dependency_labels']:
                    if dependency_label['label'] == 'case':
                        keyword += tokens[int(dependency_label['token_id'])]['form']
                        break
                break
    
        if len(topic) + len(keyword) <= 13:
            topic += keyword
        else:
            return topic

    return topic


'''
メイン
'''
if __name__ == '__main__':
    #見出し生成したいYahoo!ニュースの記事URL
    url = 'https://news.yahoo.co.jp/pickup/6354056'   

    #記事内容およびタイトルを抽出
    contents = get_contents(url)
    title = get_title(url)
    print("------------")
    print(contents)
    print("------------")
    print(title)
    print("------------")    
    
    #COTOHA APIのトークンを取得
    token = get_access_token()
    
    #記事の見出しを生成
    topic = create_news_topic(token, contents)
    print(topic)
    print("------------") 
    
    #元の見出しと生成した見出しの類似度を算出
    similarity = get_similarity(token, title, topic)['score']
    print(similarity)
    print("------------") 
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?