PHPでワードクラウドをつくる

(1/1)
ワードクラウド
ワードクラウド(word cloud)とは、与えられたコンテンツを解析し、上図のように出現頻度が高い単語ほど大きく表示する仕組みである。
今回は、与えられたURLのワードクラウドを表示するプログラムをPHPで作ってみることにする。
(2023年12月16日)サンプル・プログラムを日本語形態素解析V2に対応

目次

サンプル・プログラム

圧縮ファイルの内容
WordCloud.phpサンプル・プログラム本体。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
WordCloud.php 更新履歴
バージョン 更新日 内容
2.2.0 2023/12/16 日本語形態素解析V2対応
2.1 2021/10/02 PHP8対応,リファラチェック改良
2.1 2017/04/09 PHP7対応
2.0 2014/08/02 大幅改訂
1.1 2009/06/20 chunked対応、ぱふぅ家のホームページ以外に対応
pahooInputData.php 更新履歴
バージョン 更新日 内容
1.4.1 2023/09/30 コメントの訂正
1.4.0 2023/09/09 $_GET, $_POST参照をfilter_input()関数に置換
1.3.0 2023/07/11 roundFloat() 追加
1.2.0 2023/04/22 exitIfLessVersion() 追加
1.1.2 2023/02/05 validString() 修正

必要な機能

このプログラムでは、大きく3つの機能が要求される。
  1. URLからコンテンツを取り出す。
  2. コンテンツから単語を切り出し、出現頻度を数える。
  3. 出現頻度の高い単語ほど大きく表示する。
1.は、後述する myGetContents 関数によって実現する。2.は、「PHPで形態素解析を行う」で使ったYahoo!JAPANのWebAPI「日本語形態素解析」をそのまま利用する。
3.は、後述する 関数によって実現する。

解説:準備

  59: //Yahoo! JAPAN Webサービス アプリケーションID【各自で設定】
  60: //取得方法:https://www.pahoo.org/e-soul/webtech/php06/php06-01-02.shtm#Yahoo
  61: define('YAHOO_APPLICATION_ID', '**********************************');

Yahoo!JAPANのWebAPI「日本語形態素解析」を利用するため、Yahoo! JAPAN Webサービス アプリケーションID を取得する必要がある。その入手方法は「Yahoo!JAPAN デベロッパーネットワーク - WebAPIの登録方法」を参照されたい。
取得したIDは定数 APPLICATION_ID に格納する。

解説:ワードクラウドの作成

 258: /**
 259:  * ワードクラウドを作成する
 260:  * @param   array  $items 情報を格納した配列
 261:  * @return  string ワードクラウド(HTML)
 262: */
 263: function getWordCloud($items) {
 264:     $outstr = '';
 265:     $n = 0;
 266: 
 267:     //出現頻度の最大値・最小値を調べる
 268:     $countMax = 0;
 269:     $countMin = 0;
 270:     foreach ($items as $surface=>$val) {
 271:         if (isDisplayWord($surface, $val['pos'])) {
 272:             if ($val['count'> $countMax)      $countMax = $val['count'];
 273:             if ($val['count'< $countMin)       $countMin = $val['count'];
 274:         }
 275:     }
 276:     $k = (MAX_FONT_SIZE - MIN_FONT_SIZE) / ($countMax - $countMin);
 277: 
 278:     foreach ($items as $surface=>$val) {
 279:         if (isDisplayWord($surface, $val['pos'])) {
 280:             $n++;
 281:             $count = $val['count'];
 282:             $fsize = (int)($k * $val['count'+ MIN_FONT_SIZE);
 283:             if ($fsize < MIN_FONT_SIZE)      $fsize = MIN_FONT_SIZE;
 284:             if ($fsize > MAX_FONT_SIZE)     $fsize = MAX_FONT_SIZE;
 285:             $link = getLink($surface);
 286:             if ($n > 1)     $outstr .",\n";
 287: $outstr .=<<< EOT
 288: <span style="font-size: {$fsize}%;">
 289: <a href={$link}>{$surface}</a>
 290: </span>
 291: 
 292: EOT;
 293:         }
 294:     }
 295:     return $outstr;
 296: }

ユーザー関数 getWordCloudワードクラウドを作成する。

ある単語の表示フォントサイズ(%) \( S_n \) は下記の計算式で算出する。
\[ S_n = k C_n + S_{min} \] \[ k = \frac{S_{max} - S_{min}}{C_{max} - C_{min}} \] \( S_{max} \) ‥‥最小表示フォントサイズ(%)
\( S_{min} \) ‥‥最大表示フォントサイズ(%)
\( C_{max} \) ‥‥単語の最大出現回数
\( C_{min} \) ‥‥単語の最小出現回数
\( C_n \) ‥‥ある単語の出現回数

 222: /**
 223:  * ワードクラウド表示時における個々の単語のリンク先URLを取得する
 224:  * @param   string $surface 見出し語
 225: */
 226: function getLink($surface) {
 227:     $url  = 'https://www.google.com/search?hl=ja&lr=lang_ja&q=';    //Google検索
 228:     $site = SEARCH_DOMAIN;      //検索絞り込みドメイン名
 229: 
 230:     return $url . urlencode($surface. '+site%3A' . $site;
 231: }

ただ文字サイズを変更して並べるだけでは面白くないので、個々の単語にリンクを張ることにした。
ここでは、その単語をGoogle検索に投げ、その際に site で絞り込み検索を指定するようにした。$site には自サイトのURLを入れておけばいいだろう。

解説:コンテンツの取り出し

 138: /**
 139:  * コンテンツの必要な部分を読み込む
 140:  * @param   string $url コンテンツのURL
 141:  * @return  string 読み込んだコンテンツ/FALSE=失敗
 142: */
 143: function myGetContents($url) {
 144:     $instr = @file_get_contents($url);
 145:     $instr = mb_convert_encoding($instr, INTERNAL_ENCODING, 'auto');
 146:     if ($instr == FALSE || $instr == '')    return FALSE;
 147: 
 148:     //scriptを除く
 149:     $arr = array();
 150:     $ss = preg_match('/\<body.*?\>(.*?)<\/body\>/imsu', $instr, $arr);
 151:     $ss = preg_replace('/\<script.*?\>.*?<\/script\>/imsu', '', $arr[1]);
 152:     $ss = preg_replace('/\<style.*?\>.*?<\/style\>/imsu', '', $ss);
 153:     $ss = preg_replace('/\&nbsp\;/imsu', '', $ss);      //&nbsp;を除く
 154:     $ss = strip_tags($ss);                              //タグを除く
 155:     $ss = html_entity_decode($ss);          //特殊文字を通常文字に変換
 156:     $outstr = preg_replace('/\n|\r|\r|\t/', '', $ss);   //改行、タブを除く
 157: 
 158:     return $outstr;
 159: }

ユーザー関数 getParse に渡すコンテンツをURLから取り出す必要がある。これには少し工夫が必要だ。
まず、URLの全体を getParse に渡すのは非合理的だ。ヘッダやフッタなど、コンテンツとして分析する必要がない部分も含まれてしまう。
そこで用意したのがユーザー関数 myGetContents である。

まず、関数  file_get_contents  を使ってコンテンツ全体を変数 $instr に取り込む。
次に、 preg_match  を使って、<body> タグ内だけ切り出す。
続いて、<script> タグや<style> タグ内は省く。特殊文字 &nbsp; 無視する。
最後に、関数  strip_tags 、 html_entity_decode 、 preg_replace  を使い、HTMLやPHPのタグ、特殊文字、改行・タブなどを消去しておく。

ユーザー関数 my_get_contents については、解析したサイトの状況に合わせて変更してほしい。

質疑応答

【質問】かんちゃん 2023/12/11
define('REFER_ON', '');
アプリケーションIDを入力し、リンク先ドメイン名を入力サンプルURLをワードクラウドを作りたいページにし、以上の変更を加えたのに、上記の結果に。
まだほかに修正する個所があるのでしょうか
【回答】
プログラムが使用しているYahoo!JAPANのWebAPI「日本語形態素解析」がバージョンアップし、旧バージョンは使用できなくなりました。プログラムを新しいバージョンに対応させましたので、お試しください。
また、アプリケーションエラーを表示したときは、プログラム冒頭にある FLAG_RELEASE の定義を
 define('FLAG_RELEASE', FALSE);
としていただくことで、詳しいエラーを表示するようになります。

参考サイト

(この項おわり)
header