無駄ロボット研究所
もしあなたがポケモンだったら何なのかをRPAで明らかにする ~巡合! ポケモン診断ロボの巻
2019年8月8日 10:45
『無駄ロボット研究所』ではRPA(Robotic Process Automation)ツール「UiPath Studio」を使って役に立たない、無駄なロボットを作っていきます。業務を効率化するという本来RPAで実現すべき目的とは真逆のロボットたちをお楽しみください。
あなたが、もしも、ポケモンだったら? SNSで見かける診断サービスをRPAのロボットで実現してみませんか? 約800のポケモンの中から選び出しますので、ポケモン世代でも、「へー」とうなるようなポケモンが選び出されるかもしれませんよ。
ボクは「オムナイト」
しくみ上、実行環境によって結果が変わる可能性があるのですが、筆者の名前「清水理史」という漢字の名前で診断した結果は、次の通り、「オムナイト」でした。
雨のときにすばやさが上がる特性ですが、持っているラッキーアイテムは、すばやさが下がる「くろいてっきゅう」です。
なんて効率が悪いんでしょうか……。
というわけで、今回、作ったロボットは、SNSなどでよく見かける診断ものです。名前を入力すると、そこから、あなたと一致するポケモンを選び出してくれます。
ポケモン世代でもない限り、名前だけ言われてもピンと来ないかもしれませんが、画像を表示すると、大人の事情で怒られるのは必至なので、公開するサンプルは画像のリンク先URLだけを表示する仕様にしています。
個人で楽しむときは、サンプルのワークフローをよく見れば、簡単に改造できるはずです。
それにしても、防御力抜群ですね。実際の人間は結構打たれ弱いですけどね。
全体像の把握
さあ、今回のロボットは大作です。ワークフローは、とても長くなっています。
ロボットは、面倒なことを自動化してくれるものなので、見た目はシンプルでも、実は裏で結構、頑張ってるってのが、理想ですが、そういう意味では、今回、作るロボットは理想的かもしれません。
動かしてみると、「名前を入力して結果をブラウザで表示する」だけと、実にシンプルです。
しかし、裏でやってることは、結構、面倒な処理が多くて、REST API使っていたり、JSONからLinqで値を絞り込んで取り出していたり、無理矢理HTMLを生成していたりします。
Linqのあたりは、結構、実務でも役立ちそうなので、SQLっぽい構文でJSONから値を絞り込んで取り出しというような場合には、参考になるかと思います。
ポイントだけピックアップ
それでは、ワークフローを見ていきたいと思いますが、いかんせん、今回のワークフローは長いので、ポイントだけをピックアップして紹介することにします。ワークフロー内のアクティビティに番号を振りましたので、ワークフローを眺めながらチェックしてみてください。
なお、各アクティビティはコメントを追加してありますので、本文で触れていないものはコメントを参照してください。
①代入
今回のワークフローでは、ポケモンデータをインターネット上で公開されている「PokeAPI」から取得しています。このサービスは、次の記事の通り、ローカルのDocker環境でも動作させることができます。
本来は教育目的で公開されているものなので、あまり負荷をかけるのは好ましくありません。できれば、次の記事を参考に、ローカルで稼働させてください。
ローカルで稼働させた場合は、このアクティビティのURLをローカルのアドレスに変更します。
②代入
2つめの代入から、しばらくの間は、乱数からポケモンを選び出す処理です。
ユーザーによって入力された名前を「system.Text.Encoding.GetEncoding(932).GetBytes(str)」によって、文字コードの数値に分解し、数値の合計を乱数生成用のシード値として使います。
選び出されるポケモンが毎回違うと診断にならないので、シード値を固定して、基本的に同じ名前なら、毎回、同じポケモンが選び出されるようにしています。
同じしくみで、ラッキーアイテムとして選び出すアイテムの番号も乱数で生成します。
③HTTPリクエスト
ポケモンとアイテムの番号を乱数で生成したら、その番号を指定して、前述したPokeAPIから、ポケモンとアイテムのデータを取得します。
このAPIはシンプルで、「https://pokeapi.co/api/v2/pokemon/138」のように、エンドポイントURLに、利用するサービス(ここではポケモンの基本情報なので“/pokemon”)を指定し、その後に、ポケモンの名前かIDを指定します。
なので、HTTPリクエストのURLとしては、変数を使って「endPoint+"pokemon/"+pokeId」のように表します。
「endPoint」変数が代入で入力したエンドポイントのURL、「pokeId」変数が名前から乱数で生成したポケモンIDです。
ちなみに、返答はJSONで戻ってくるので、「HTTPリクエスト」の結果を「JSONをデシリアライズ」で再構築しておきます。
ワークフローには同じような処理がいくつか並んでいます。各処理の目的は次のようになります。
- /pokemon:ポケモンの基本情報を取得
- /pokemon-species:ポケモンの詳細情報を取得(日本語名などを取得)
- /item:アイテムを取得
④シーケンス ポケモン
シーケンスとして、まとめた部分になります。今回のポイントのひとつです。
ここでは、Linqの使い方が重要になります。ポケモンの詳細な情報は、「pokemon-species」から取得したJSONに格納されています。
たとえば、一部のみを抜粋すると、次のようになりますが、ここには、ポケモンの詳細な情報が、各国の言語ごと、さらにポケモンのバージョンごとに、格納されています(1体のポケモンで1,000行近くある!)。
{
"base_happiness": 70,
"capture_rate": 45,
"color": {
"name": "blue",
"url": "https://pokeapi.co/api/v2/pokemon-color/2/"
},
"egg_groups": [
{
"name": "water3",
"url": "https://pokeapi.co/api/v2/egg-group/9/"
},
{
"name": "water1",
"url": "https://pokeapi.co/api/v2/egg-group/2/"
}
],
"evolution_chain": {
"url": "https://pokeapi.co/api/v2/evolution-chain/69/"
},
"evolves_from_species": null,
"flavor_text_entries": [
{
"flavor_text": "生活在古代海洋中的宝可梦。\n好像曾是始祖大?的食物,\n????有?痕的化石。",
"language": {
"name": "zh-Hans",
"url": "https://pokeapi.co/api/v2/language/12/"
},
"version": {
"name": "ultra-sun",
"url": "https://pokeapi.co/api/v2/version/29/"
}
},
{
"flavor_text": "古代の海に 生きていた ポケモン。\nアーケオスの エサだったようで\n歯型のついた 化石が 見つかる。",
"language": {
"name": "ja",
"url": "https://pokeapi.co/api/v2/language/11/"
},
"version": {
"name": "ultra-sun",
"url": "https://pokeapi.co/api/v2/version/29/"
しかしながら、実際に必要なデータは日本語の、特定のバージョンのみです。なので、JSONの中から、値を絞り込んで、必要なデータだけを取り出します。
例として、上のJSONの例を参考に、ポケモンの説明文を取り出す処理を見てみましょう。左辺に「pokeFlavor」が指定された代入アクティビティの右辺には、次のようになっています。
(From p In pokeSPjson("flavor_text_entries")
Where Convert.ToString(p("version")("name"))="omega-ruby" AndAlso Convert.ToString(p("language")("name"))="ja"
Select Convert.ToString(p("flavor_text"))).First.ToString
データベースをかじったことがある人は、そのときに学習したSQL文と似ていると思うかもしれませんが、これが統合言語クエリ「Linq」です。.NET Frameworkで提供される機能で、VB.netの中で、SQL文と同じようなFromやWhereといった句を使えます。
各部分を分解して見ていくことにしましょう。
(From p In pokeSPjson("flavor_text_entries")
ここでは、データの出典を指定しています。pokeSPjsonは、/pokemon-spicesへのリクエストで取得した情報をデシリアライズしたJSONデータです。
上のJSONでも確認出来るとおり、説明分はpokeSPjsonという全体データの中の「"flavor_text_entries"」というエントリに記載されています。なので、まずは、この部分を対象範囲として指定します。
Where Convert.ToString(p("version")("name"))="omega-ruby" AndAlso Convert.ToString(p("language")("name"))="ja"
続いての「Where」句では、データの場所を指定します。ポケモンの説明は、「"flavor_text_entries"」エントリに記載されていますが、ここには、中国語、英語、日本語の各国のデータ、さらにバージョンごとのデータが含まれています。
このため、条件を指定して絞り込んでいます。条件は、AndAlsoで結んだ2つです。
ひとつは、「"version"」の「"name"」が「"omega-ruby"」である値。もう1つは、「"language"」の「"name"」が「"ja"」である値です。
Select Convert.ToString(p("flavor_text"))).First.ToString
最後に、「Select」句で取得する値を指定します。上のJSONでも確認できる通り、最終的に欲しい値は「flavor_text」です。
このように、Linqを使うと、複雑な構造のJSONデータから、条件を指定して特定のデータをピンポイントで取り出すことができます。
この後、「とくせい」シーケンスや「ワザ」シーケンスなど、さらに処理は続きますが、基本的に、同じようなLinqによるデータ取得の繰り返しとなります。実際のワークフローで処理を確かめてみてください。
⑤テキストファイルを書き込む(HTML生成-画像URLのみ)
最後に、ここまでのアクティビティで取得した情報をHTMLとして出力します。
UiPathには、HTMLを出力するアクティビティはないのですが、「テキストファイルを書き込む」アクティビティを使って、手動でHTMLタグを指定すれば、HTMLの出力も可能です。
HTMLの記述方法は好みにまかせますが、基本的には、次のように変数以外の部分を「""」で囲み、「+」を使って変数と文字列をつないでいきます。
"<body><p><h1>"+str+"さんは、"
ポイントとなるのは、HTML内で使われる「""」記号です。このように、「""」内で使う「"」記号は「""」と二重で指定します。たとえば、次の文字コードの部分のようになります。
"<!DOCTYPE html><meta http-equiv=""content-type"" charset=""utf-8""><html lang=""ja""><head><meta charset=""utf-8""><title>PokeAPIを使ったポケモン診断</title></head>"
なお、「UiPath Studio」の制限かもしれませんが、行数が多くなると、「""」の対応を見失うことがあるようです。このため、ここでは4行に収めています。
次のように、同じ内容でも7行を超えると「!」が表示されエラーになるので、多少 見にくくなりますが、少ない行数で収めておくのがコツです。
というわけで、ポイントのみ解説しました。Linqのあたりは、JSONの構造を見ながらでないとわかりにくいので、参考としてサンプルワークフローに「pokemonSPdata.txt」という、上で解説したJSONデータを入れておきます。これを見ながら、ワークフローを参照するといいでしょう。
JSONの解析は根気がいる作業ですが、がんばってチャレンジしてください。