無駄ロボット研究所

もしあなたがポケモンだったら何なのかをRPAで明らかにする ~巡合! ポケモン診断ロボの巻

『無駄ロボット研究所』では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の解析は根気がいる作業ですが、がんばってチャレンジしてください。