まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:xml

Node.js で XML を扱うのは難しいので、いったん JSON に変換してから扱うことが多いと思っています。そんな場合に自分はよく xml2json というライブラリを使っていました。

xml2json はその名前の通り、XML を JSON に変換してくれるパーサーを実装したライブラリです。使い方は例えば以下のような感じで、XML 文字列をそのまま引数にして実行すると、結果が JSON 文字列となって戻してくれます(JSON の文字列ではなく JSON オブジェクトとして扱う場合は更に JSON.parse() を実行します)。挙動も速く、便利に使っていました:
  :
var parser = require( 'xml2json' );
  :

  :
var xmlStr = fs.readFileSync( xml_filename, 'utf-8' );   //. ファイル名を指定して XML 文字列を取得
var jsonStr = parser.toJson( xmlStr );                   //. XML 文字列を JSON 文字列に変換
var jsonObj = JSON.parse( jsonStr );                     //. JSON 文字列を JSON オブジェクトに変換
  :

ところが上述のような処理を記述した Node.js のソースコードを Windows 環境下で実行しようとした時に問題が発生しました。実行前のライブラリインストール(npm install)の段階でエラーが発生してしまうのでした。詳細なエラーメッセージ等は後述のリンク先を参照していただきたいのですが、node-expat という xml2json の依存ライブラリを node-gyp でビルドする際に何やらエラーが発生していました。

で、この現象を調べた所、どうやら Windows 環境下では xml2json ライブラリ自体がビルドできないという根本的な問題を抱えているようでした:
npm install xml2json error


※厳密には「ビルドできない」わけではないけど、別途 Python や Visual Studio C++ 2012 などの他にインストールする必要があるツールが多く存在しているようです。


↑リンク先でも回避策として「別の XML -> JSON 変換ライブラリを使う」ことが提案されています。というわけで Windows でも使える同機能のライブラリを探したところ、単純な変換であれば fast-xml-parser が使えそうでした。

fast-xml-parser を使う場合は、上述の内容は以下のようなコードになります:
  :
var parser = require( 'fast-xml-parser' );
  :

  :
var xmlStr = fs.readFileSync( xml_filename, 'utf-8' );   //. ファイル名を指定して XML 文字列を取得
var jsonObj = parser.parse( xmlStr );                    //. XML 文字列を JSON オブジェクトに変換
  :

現実問題として自分個人の開発環境として Windows がベースとなることは今のところ考えにくいのですが、(WSL とかではなく)Windows 環境下で開発しないといけない人との共同作業が発生するようなケースではこういったことも意識しないといけないこともでてくると感じています。


画像を表現するフォーマットの1つに SVG(Scalable Vector Graphics) があります。いわゆる「ベクトルデータ」なので、拡大縮小時にもなめらかな曲線で描くことが可能になる、という特徴があります。

この SVG 、実体は特定のルールで記述された XML データとなります。例として以下は 100x100 の2次元エリア内に黒い塗りつぶし三角形を描画しています:
<svg width="100" height="100">
<path d="M0 100 L100 100 L100 0 Z" style="fill:black"/>
</svg>

このような XML データを image/svg+xml の Content-Type を指定して返すことで画像データを動的に生成してクライアントに返すことが可能になります(以下は Node.js での例):
  :

app.get( '/img.svg', function( req, res ){
  res.contentType( 'image/svg+xml' );
  var svg = '<svg width="100" height="100">';
  svg += '<path d="M0 100 L100 100 L100 0 Z" style="fill:black"/>';
  svg += '</svg>';
  res.write( svg );
  res.end();
});

  :

理論的にはこの動的画像を HTML の <img> タグで指定して表示することができる、はず、です:
  :
<img src="/img.svg"/>
  :

ところがこの方法だと正しく表示できません(画像として認識されないようです):
2018053001


<img> タグの src 属性に埋め込んで指定する場合、SVG 側でも xmlns 属性を指定して生成する必要があるようです。つまり SVG 側を以下のように変更します:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<path d="M0 100 L100 100 L100 0 Z" style="fill:black"/>
</svg>

コードの場合も同様に変更します:
  :

app.get( '/img.svg', function( req, res ){
  res.contentType( 'image/svg+xml' );
  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">';
  svg += '<path d="M0 100 L100 100 L100 0 Z" style="fill:black"/>';
  svg += '</svg>';
  res.write( svg );
  res.end();
});

  :

こうした上で <img src="/img.svg"/> とすると正しく表示できるようになりました:
2018053002



無料のグループウェアを SaaS で提供していたサイボウズ Live が 2019 年4月15日をもってサービスを終了する、というアナウンスがありました:
サイボウズLiveサービス終了のお知らせ

個人的にも仲間内サークル活動の中で使っていたりしていたもので、このニュース自体はとても残念なものでした。が、終了まで1年半も猶予を持ったアナウンスであり、また(上記リンクによると)今後データのエクスポート(CSVファイル?機能?)などを提供する予定もあるらしいです。データを他のサービスに移行するための準備期間としては充分にあるようにも感じました。

データ移行に関しては時間的な猶予もあるので、しばらくは謹製のデータエクスポートが出されるのを待ってもいいとは思っています。が、個人的/技術的な興味もあって自分でも PHP で作ってみました。詳しくは後述しますが、もしご利用になる場合、現時点ではまだ不完全なものだと理解の上で使ってください。なおこのツールではデータを XML ファイルでエクスポートします(掲示板データなど、改行情報を含むデータは CSV に向かないと判断したので):
https://github.com/dotnsf/cbl_export/blob/master/cbl_export.php


実際に使うには、まず PHP 実行環境が必要です(自分自身がテストした範囲では PHP 5.3.3 でも動きました、かなり古いバージョンでも大丈夫だと思います):
(例 CentOS の場合)
# yum install php php-mbstring php-xml php-pear

加えてプログラムの中で OAuth などの外部ライブラリモジュールを使うため、これらのモジュールをあらかじめインストールしておいてください:
(例 pear を使う場合)
# pear install Net_URL2
# pear install HTTP_Request2
# pear install HTTP_OAuth

またこのツールを使う準備としてサイボウズ Developer Center のアカウントが必要です。同アカウントをお持ちでない場合はリンク先の「デベロッパー登録」ボタンから申請してアカウントを作成してください:
2017102901


アカウント作成後にログインし、「Myアプリケーション」の一覧で「アプリケーションを登録する」を選択し、今から動かすアプリケーションの登録を行います:
2017102902


アプリケーションを登録する際のアプリケーション名などは任意に設定いただいていいのですが、「アプリケーションの種類」は「クライアント」、そして「アクセスレベル」は「レベルZ」を選択する点に注意してください(ここを間違えると正しく動かなくなります。なおアプリケーション種類をクライアントにしないとレベルZのアクセスレベルは選択できないはずです):
2017102903


こうしてアプリケーションを登録し、登録後の画面に表示される Consumer Key と、Consumer Secret をメモしておきます(他人に教えてはいけません)。またアプリケーションの種類とアクセスレベルが正しく登録されていることを確認してください:
20171026



ここまでの準備ができたところで、改めて今回作成したエクスポートアプリを使います。こちらのサイトから git clone するなどして cbl_export.php をコピーします:
https://github.com/dotnsf/cbl_export/blob/master/cbl_export.php


cbl_export.php をテキストエディタで開き、一部を編集します:
<?php
//. cbl_export.php
//. Referer: https://developer.cybozulive.com/doc/current/#id1
//. For XAuth(Ones of Level Z)
$consumer_key           = '(CybozuLive Developer Center Consumer Key)';
$consumer_secret        = '(CybozuLive Developer Center Consumer Secret)';
$xauth_access_token_url = 'https://api.cybozulive.com/oauth/token';
 
$params = array(
    'x_auth_username' =--> '(CybozuLive Email)',
    'x_auth_password' => '(CybozuLive Password)',
    'x_auth_mode'     => 'client_auth',
);

:
:

上記の青字部分を編集します。$consumer_key には上記サイボウズ Developer Center でのアプリケーション登録時に確認した Consumer Key を、$consumer_secret には同 Consumer Secret を代入します。また $params 'x_auth_username' には自分のサイボウズ Live ログイン時のメールアドレスを、そして 'x_auth_password' には同パスワードを指定します。


ここまでの準備ができたら php コマンドで cbl_export.php を実行します:
# php -f cbl_export.php

実行が成功するとカレントディレクトリに自分のメールアドレスと同じ名前のフォルダと、自分がサイボウズ Live で所属しているグループごとの ID(X:XXXXX みたいなフォーマットです)のフォルダが作成され、各データがそれぞれのフォルダ内にエクスポートされます。例えば1つのグループにしか所属していない場合であれば、自分のメールアドレスのフォルダが1つと、そのグループのIDのフォルダが1つ生成されます:
2017102904
(↑自分が実行した場合の結果サンプル)


そして個人のデータはメールアドレスのフォルダ内に、要素毎に XML ファイルでエクスポートされています(現在の仕様ではスケジュール、チャット、ToDo、コネクションのデータがエクスポートされます):
2017102905


またグループ ID のフォルダには各グループ毎のメンバー、掲示板、イベント、ToDo、ファイル共有の情報が XML ファイルでエクスポートされています:
2017102906


なお、このツールを開発するにあたり、サイボウズ Live API を使っています。その仕様はこちらです:
サイボウズLive データ API ドキュメント


(↓2017/Oct/29 時点での制限事項)
上記のように一応動くものが作れたつもりなのですが、2017/Oct/29 時点ではファイルのダウンロード機能が正しく動いていません。例えばサイボウズ Live のファイル共有機能や掲示板の中でファイルを添付しているケースは珍しくないと思っています。その「どんなファイルが添付されているか?」といった情報は取得できて、その情報は現在取得できるファイルの中にも正しくエクスポートできているのですが、肝心のファイルそのもののダウンロードができていません。具体的にはこの API の実行時にだけ想定外のエラーになってしまい、まだその理由や解決策が分からずにいます:
https://developer.cybozulive.com/doc/current/pub/fileDownload.html


この部分については私のミスなのか、API 側の問題なのかの判別ができず、現在は問い合わせ中です。分かり次第に対応するつもりです。
(↑2017/Oct/29 時点での制限事項)


(↓2017/Nov/06 追記)
サイボウズより返答あり。サイボウズ側では問題なく動いているとのこと。むむむ・・ (--;
(↑2017/Nov/06 追記)


添付ファイルのダウンロード以外についてはある程度動くようになっているつもりです。早めのデータ移行を検討していたり、移行先候補へのテストを早めに実行したい場合に活用ください。

なおサイボウズから謹製のエクスポートデータやエクスポートツールが提供された場合は、(きっと面倒なアカウント登録なども不要になると思うので)そちらの利用をオススメします。 (^^;


(↓2017/Dec/19 追記)
kintone エバンジェリストでもある長井様( @akvabit)からコメントをいただき、現在ダウンロード API は画像ファイルのみが対象となっていることがわかりました。つまり画像以外のファイルは現時点では API ではダウンロードできないようです。
併せて長井様からのコメントを参考に PHP ファイルを改良しました。現在は「画像ファイルについてはダウンロードする(画像以外のファイルは無視する)」ような仕様にした上で動いています。
(↑2017/Dec/19 追記)

(↓2017/Dec/24 追記)
ファイルダウンロード機能ですが、動いていたように思えたのですが、やはり正しく動いていませんでした。ロジックを間違えていたせいで気付かなかったのですが、元のエラーが取れていませんでした(画像ファイルを対象にしても同じエラーが発生しています)。
(↑2017/Dec/24 追記)

久しぶりに PHP を使っていて気付いたことをブログネタにしました。

最近の Web API/REST API は JSON フォーマットで結果を取得できるものが多いですが、中には XML のみサポートされているものもあります(RSS とかね)。そんな XML を PHP で扱う方法について調べました。
2017102700


まず、PHP には simplexml_load_file および simplexml_load_string という便利な関数が用意されています。これらは XML ファイル(のURL)や XML 文字列を指定して、PHP のオブジェクト化するというものです。例えば以下のような ATOM フィードフォーマットの XML ファイル(atom.xml)があったとします:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
  <id>tag:phpxml/</id>
  <title>サンプルタイトル</title>
  <link rel="alternate" type="text/html" href="http://sample.xml.jp/feed/"/>
  <link rel="self" type="application/atom+xml" href="http://sample.xml.jp/feed/atom10.xml"/>
  <entry>
    <id>http://sample.xml.jp/test1.html</id>
    <title>紹介タイトルその1</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test1.html"/>
    <summary>紹介するエントリの1つです。</summary>
    <dc:updated>Thu, 26 Oct 2017 07:00:00 +0900</dc:updated>
  </entry>
  <entry>
    <id>http://sample.xml.jp/test2.html</id>
    <title>紹介タイトルその2</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test2.html"/>
    <summary>紹介するエントリの2つめです。</summary>
    <dc:updated>Fri, 27 Oct 2017 07:00:00 +0900</dc:updated>
  </entry>
</feed>

この atom.xml ファイルに対して、例えば以下のようなコード(test1.php)を用意して実行します:
<?php
$feed = simplexml_load_file( "atom.xml" ); echo "id: " . ( $feed->id ) . "\n"; echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){ echo "entry_title: " . ( $entry->title ) . "\n"; echo "link_url: " . ( $entry->link->attributes()->href ) . "\n"; }
?>

その実行結果(赤字部分)はこちら:
$ php -f test1.php

id: tag:phpxml/
title: サンプルタイトル
entry_title: 紹介タイトルその1
link_url: http://sample.xml.jp/test1.html
entry_title: 紹介タイトルその2
link_url: http://sample.xml.jp/test2.html

これだけ。simplexml_load_file 関数や simplexml_load_string 関数は XML ファイルや XML 文字列を簡単に PHP のオブジェクト化して、XML 内の各データや属性値にアクセスできるようになるものです。

※上記のように属性値は $entry->link->attributes()->属性名 で取得できます。


さてここまでは普通にできるのですが、実はこの方法では取得が難しいデータがあります。各 entry 内にある <dc:updated> という特殊な名前のデータです。 
      :
      :
    <title>紹介タイトルその1</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test1.html"/>
    <summary>紹介するエントリの1つです。</summary>
    <dc:updated>Thu, 26 Oct 2017 07:00:00 +0900</dc:updated>
      :
      :

この部分が少し厄介なのです。test1.php と全く同じ要領でこんな test2.php ファイルを作ってみます:
<?php
$feed = simplexml_load_file( "atom.xml" ); echo "id: " . ( $feed->id ) . "\n"; echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){ echo "entry_title: " . ( $entry->title ) . "\n"; echo "link_url: " . ( $entry->link->attributes()->href ) . "\n"; echo "dc:updated: " . ( $entry->dc:updated ) . "\n"; }
?>

この test2.php を実行すると・・・ Parse error となってしまいます。PHP の文法ではここに ":" を記述してはいけないんですね。。
$ php -f test2.php

Parse error: syntax error, unexpected ':' in test2.php on line 8

なるほど。これでは文法的にNGということはわかった。が、問題はどうやって回避すればいいのか、です。simplexml_load_file 関数を使わずに別の方法で XML をパースする方法もありますが、それはそれで面倒だったりします。なんとか simplexml_load_file の便利な機能をそのままに回避できないか・・

そこでググって見つけたがこの方法でした。簡単にいうと
 (1) タグ(<から>まで)内の ":" を "_" とかに一括変換する
 (2) ただしプロトコル( http:// とか https:// とか)で使われている部分だけは元に戻す
という方法です。この方法で作り直したのがこちら(test3.php)です:
<?php
//. XML ファイルを文字列変数へ読み込む
$txt = file_get_contents( "atom.xml" );
//. <***:****> -> <***_****>
$txt  = preg_replace( "/<([^>]+?):(.+?)>/", "<$1_$2>", $txt );
//. プロトコルは元に戻す
$txt  = preg_replace( "/_\/\//", "://", $txt );

$feed = simplexml_load_string( $txt );
echo "id: " . ( $feed->id ) . "\n";
echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){
  echo "entry_title: " . ( $entry->title ) . "\n";
  echo "link_url: " . ( $entry->link->attributes()->href ) . "\n";
  echo "dc:updated: " . ( $entry->dc_updated ) . "\n";
}
?>

青字部分ではまず普通に atom.xml ファイルを文字列として読み込み、上記 (1) および (2) の処理を行います。そしてその結果を simplexml_load_string 関数でオブジェクト化しました。このようにすると、各 entry 内の <dc:updated> 要素は <dc_updated> と変わっているので、そのまま扱えるようになります。

この test3.php を実行すると、期待通りの結果を得ることができました:
$ php -f test3.php

id: tag:phpxml/
title: サンプルタイトル
entry_title: 紹介タイトルその1
link_url: http://sample.xml.jp/test1.html
dc:updated: Thu, 26 Oct 2017 07:00:00 +0900
entry_title: 紹介タイトルその2
link_url: http://sample.xml.jp/test2.html
dc:updated: Fri, 27 Oct 2017 07:00:00 +0900

とりあえず動くことは動きました。でももうちょっと上手い方法がないもんかな。。

このエントリの続きです。取得した XML の内容を詳しくみてみます。


改めて、まず API に入力したテキストは以下の様なものでした:
John Smith lives in New York, and he has been living there since 2001.


そして、API で取得した解析結果の XML はこのようなものでした:
<?xml version='1.0' encoding='utf-8'?>
<rep sts="OK">
<doc id="">
  <text>John Smith lives in New York, and he has been living there since 2001.
</text>
  <sents>
    <sent sid="0" begin="0" end="15">
      <text>John Smith lives in New York, and he has been living there since 2001.</text>
      <parse score="-2.73025">[S [S [NP John_NNP Smith_NNP NP] [VP lives_VBZ [PP in_IN [NP New_NNP York_NNP NP] PP] VP] S] ,_, and_CC [S [NP he_PRP NP] [VP has_VBZ [VP been_VBN [VP living_VBG [ADVP there_RB ADVP] [PP since_IN [NP 2001_CD NP] PP] VP] VP] VP] S] ._. S]</parse>
      <dependency_parse>John NNP 1 -I Smith NNP 2 NP lives VBZ -1 VP in IN 2 PP New NNP 5 -I York NNP 3 NP , , 2 -E and CC 2 -E he PRP 9 NP has VBZ 2 VP been VBN 9 VP living VBG 10 VP there RB 11 ADVP since IN 11 PP 2001 CD 13 NP . . 2 -E </dependency_parse>
      <usd_dependency_parse>John NNP 1 dep Smith NNP 2 npmod lives VBZ -1 root in IN 5 case New NNP 5 dep York NNP 2 nmod , , 2 dep and CC 2 dep he PRP 9 npmod has VBZ 2 VP been VBN 9 VP living VBG 10 VP there RB 11 ADVP since IN 14 case 2001 CD 11 nmod . . 2 dep</usd_dependency_parse>
      <tokens>
        <token tid="0" begin="0" end="3" type="0">John</token>
        <token tid="1" begin="5" end="9" type="0">Smith</token>
        <token tid="2" begin="11" end="15" type="0">lives</token>
        <token tid="3" begin="17" end="18" type="0">in</token>
        <token tid="4" begin="20" end="22" type="0">New</token>
        <token tid="5" begin="24" end="27" type="0">York</token>
        <token tid="6" begin="28" end="28" type="2048">,</token>
        <token tid="7" begin="30" end="32" type="0">and</token>
        <token tid="8" begin="34" end="35" type="0">he</token>
        <token tid="9" begin="37" end="39" type="0">has</token>
        <token tid="10" begin="41" end="44" type="0">been</token>
        <token tid="11" begin="46" end="51" type="0">living</token>
        <token tid="12" begin="53" end="57" type="0">there</token>
        <token tid="13" begin="59" end="63" type="0">since</token>
        <token tid="14" begin="65" end="68" type="0">2001</token>
        <token tid="15" begin="69" end="69" type="6144">.</token>
      </tokens>
    </sent>
  </sents>
  <mentions>
    <mention mid="-M0" mtype="NAM" begin="0" end="9" head-begin="0" head-end="9" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.995606" corefScore="1">John Smith</mention>
    <mention mid="-M1" mtype="NAM" begin="20" end="27" head-begin="20" head-end="27" eid="-E1" etype="GPE" role="LOCATION" metonymy="0" class="SPC" score="0.993534" corefScore="1">New York</mention>
    <mention mid="-M2" mtype="PRO" begin="34" end="35" head-begin="34" head-end="35" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.955652" corefScore="0.417012">he</mention>
    <mention mid="-M3" mtype="NONE" begin="65" end="68" head-begin="65" head-end="68" eid="-E2" etype="DATE" role="DATE" metonymy="0" class="SPC" score="0.820601" corefScore="1">2001</mention>
  </mentions>
  <entities>
    <entity eid="-E0" type="PERSON" generic="0" class="SPC" level="NAM" subtype="OTHER" score="0.645765">
      <mentref mid="-M0">John Smith</mentref>
      <mentref mid="-M2">he</mentref>
    </entity>
    <entity eid="-E1" type="GPE" generic="0" class="SPC" level="NAM" subtype="OTHER" score="1">
      <mentref mid="-M1">New York</mentref>
    </entity>
    <entity eid="-E2" type="DATE" generic="0" class="SPC" level="NONE" subtype="OTHER" score="1">
      <mentref mid="-M3">2001</mentref>
    </entity>
  </entities>
  <relations version="KLUE2_cascaded:2011_10_25">
    <relation rid="-R1" type="residesIn" subtype="OTHER">
      <rel_entity_arg eid="-E0" argnum="1"/>
      <rel_entity_arg eid="-E1" argnum="2"/>
      <relmentions>
        <relmention rmid="-R1-1" score="0.525653" class="SPECIFIC" modality="ASSERTED" tense="UNSPECIFIED">
          <rel_mention_arg mid="-M0" argnum="1">John Smith</rel_mention_arg>
          <rel_mention_arg mid="-M1" argnum="2">New York</rel_mention_arg>
        </relmention>
      </relmentions>
    </relation>
  </relations>
  <sgml_sent_info/>
  <sgml_char_info/>
</doc>
</rep>

この XML を上から眺めていくと、入力データである "John Smith lives in New York, and he has been living there since 2001." が解析されて、トークンに分類され、それぞれどういった品詞なのかの分析がされ、・・・ という経緯が分かると思います。

その中に以下の <mentions> 要素が見て取れます:
  <mentions>
    <mention mid="-M0" mtype="NAM" begin="0" end="9" head-begin="0" head-end="9" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.995606" corefScore="1">John Smith</mention>
    <mention mid="-M1" mtype="NAM" begin="20" end="27" head-begin="20" head-end="27" eid="-E1" etype="GPE" role="LOCATION" metonymy="0" class="SPC" score="0.993534" corefScore="1">New York</mention>
    <mention mid="-M2" mtype="PRO" begin="34" end="35" head-begin="34" head-end="35" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.955652" corefScore="0.417012">he</mention>
    <mention mid="-M3" mtype="NONE" begin="65" end="68" head-begin="65" head-end="68" eid="-E2" etype="DATE" role="DATE" metonymy="0" class="SPC" score="0.820601" corefScore="1">2001</mention>
  </mentions>

Watson が解析した結果、0文字目から9文字目までの "John Smith" は 99.5606% の確率で一人の人間(PERSON)の名前(NAM)であり、同様に "New York" は 99.3534% の確率で1つの場所(LOCATION)の名前(NAM)だと解析しています。

また "he" は人間(PERSON)の代名詞(PRO)であり、かつ "John Smith" と同じ eid の値("-E0")になっていて、その関連確率は 41.7012% になっています。つまりこの "he" は約40%の確率で "John Smith" のことだ、と判断していることになります。この文脈だとその通りなので、ちゃんとその辺りを正しく判断できていることになります。

そして文章の最後にある "2001" という数字はただの数字ではなく、この文脈の中では 82.0601% の確率で日付(DATE)の一部、つまり「年」であると判断しています。これもおそらく前後関係からの判断だと思いますが、実際正しいですよね。


そして、これら名詞の解析結果が以下の <entities> に記載されている内容で出力されています:
 
  <entities>
    <entity eid="-E0" type="PERSON" generic="0" class="SPC" level="NAM" subtype="OTHER" score="0.645765">
      <mentref mid="-M0">John Smith</mentref>
      <mentref mid="-M2">he</mentref>
    </entity>
    <entity eid="-E1" type="GPE" generic="0" class="SPC" level="NAM" subtype="OTHER" score="1">
      <mentref mid="-M1">New York</mentref>
    </entity>
    <entity eid="-E2" type="DATE" generic="0" class="SPC" level="NONE" subtype="OTHER" score="1">
      <mentref mid="-M3">2001</mentref>
    </entity>
  </entities>

更に、これらの各名詞同士の関係として <relations> に記載される関連性が出力されています:
 
  <relations version="KLUE2_cascaded:2011_10_25">
    <relation rid="-R1" type="residesIn" subtype="OTHER">
      <rel_entity_arg eid="-E0" argnum="1"/>
      <rel_entity_arg eid="-E1" argnum="2"/>
      <relmentions>
        <relmention rmid="-R1-1" score="0.525653" class="SPECIFIC" modality="ASSERTED" tense="UNSPECIFIED">
          <rel_mention_arg mid="-M0" argnum="1">John Smith</rel_mention_arg>
          <rel_mention_arg mid="-M1" argnum="2">New York</rel_mention_arg>
        </relmention>
      </relmentions>
    </relation>
  </relations>

eid の "-E0" と "-E1"、つまり "John Smith" と "New York" の間は 52.5653% の確率で "residesIn"、つまり「常設する/住んでいる」の関係がある、と判断しています。これは "living" あたりから判断しているのだと思いますが、文脈としても正しい判断ができていますね。。


今回は簡単なサンプルを使ったので、結果も比較的説明しやすいものでした。ただこの結果を見る限りでは、入力した文章は正しく解析され、各単語の関係性もかなり正しく分析してくれているようです。 Watson はアメリカのクイズ王に勝った時に「クイズ問題文章を解析して、何が問われているのかを分析した上でその答を導き出し、その確信度がある程度の確率を上回った時に回答する」という解説を見たのですが、その仕組みの一端を垣間見たような気がしますね。。


これだけのサービスが言語限定とはいえ、IBM Bluemix を通じて無料で提供されているのはなかなか凄いことだと思います。これだけの機能を自前で実装するにはかなり困難が伴うでしょうが、今は無料サービスとして、誰でも使える形で提供されていることになります。

こうなると日本語サービスの提供が待ち遠しいです。もちろん言語としての難易度は全然違うでしょうけど、






 

このページのトップヘ