Power Automate Desktop(PAD)が楽しくて、ここ数日はちょっと空き時間があるといろいろと自動化を試しています。
自宅で引退しかけた古いPCを使ってお手軽にデータのマイニングをしてみたり、APIをつかってWebサービスを触ってみたり、イメージしたフローをサクサクとトライアンドエラーで組み上げられるのが非常に快適です。
一方でちょっと古いPCで起こりがちなエラーや謎の例外発生など、まだちょっと完璧じゃないところもちらほらと。
のちのち改善されるかとは思いますが、現段階で気になっている悩みごとや気づいたことなどをまとめてみたいと思います。
本エントリは多少のプログラミング知識を前提とした内容となっています。また、クラベルの個人的な感想や使用してみての印象を基に記述した個所もあり、Power Automate Desktopの正式な仕様をお伝えするものではありませんのでご了承ください。
また、こちらではPower Automate DesktopでのアクションをRobinコードで記述しています。(普段はスクリーンショットを作ったりしてますが、今回は手抜きです。)コードはコピペで使用できますので、よろしければ試してみてください。
Power Automate Desktop:作成したフローを他のユーザーに共有する方法(補足あり)
また、Power Automate Desktopでの自動化に初めてチャレンジされる方向けのコンテンツは当ブログのPower Automate Desktopカテゴリにあります。こちらはサンプルフローやスクリーンショットを交えてやさしく解説していますのでぜひご覧ください。
ブラウザのインスタンス化に失敗して困り中
Webオートメーションでアクセスしたページから情報を取得する場合、「新しい(ブラウザ)を起動する」アクションで新しいブラウザを起動したり、別途開かれたタブに接続してインスタンス化するのですが、このアクションが結構失敗します。
インスタンス取得のタイムアウト(?)があるのか、特にスペックの低いPCで割と頻繁に起こりがちなのですが、最新のハイエンドPCでも起こることがあるため、処理スピードだけの問題だけでもなさそうな印象です。
また、インスタンス化できなかった場合でもエラー処理でフローを継続することができますが、過去に開いたタブに再度接続して操作するような動作はなかなかうまくいきません。
例えば、あるWebサービスがあるデータの集合を基にそれぞれ新しいタブを開く動作(target=”_blank”)になっており、そのひとつひとつのタブに処理を行っていくような場合、新しく開くタブをそのままにしておくとタブが無限に増えてしまいます。
そのため、新しいタブを開いた直後に「新しい(ブラウザ)を起動する」を使ってタブに接続してインスタンス化を行い、処理が終わったら「Webブラウザーを閉じる」を使ってそのタブを閉じるようにする(最初のひとつのタブだけが残り続ける)のですが、これを繰り返していると前述のインスタンス化の失敗が結構起こります。
# Webブラウザを起動して %Brouser% インスタンスを取得
WebAutomation.LaunchEdge Url: URL WindowState: WebAutomation.BrowserWindowState.Normal ClearCache: False ClearCookies: False BrowserInstance=> Browser
# タブから情報を取る
# ~
# 各項目で繰り返し処理する
LOOP FOREACH CurrentItem IN DataList
# UI要素をクリックする(target="_blank"でページが開く)
WebAutomation.Click BrowserInstance: Browser Control: appmask['']
# 開かれたタブ(アクティブウィンドウ)に接続して %Brouser2% インスタンスを取得
# ここが失敗するのでとりあえずエラーでも進むようにする
WebAutomation.AttachToTheForegroundEdge BrowserInstance=> Browser2
ON ERROR
END
# ここでいろいろな処理を実施
#~
# %Brouser2% のタブを閉じる ↓ これを確実に行いたい
WebAutomation.CloseWebBrowser BrowserInstance: Browser2
END
途中でエラーが発生してしまうと%Brouser2%
に古いインスタンスが残ってしまうため、下記のような感じで%Brouser2%
が空じゃない場合に開放するような処理を入れてみてもうまく回らず、気づいたらタブだらけになってる、ということが結構あります。
IF IsNotEmpty(Browser2) THEN
WebAutomation.CloseWebBrowser BrowserInstance: Browser2
END
タブへの接続方法がほかにも用意されているため他のやり方ならうまくいくのかもしれませんが、現状確実にエラー処理もこなしてインスタンスの取得と解放ができるフローにはできておらず、引き続き課題になってます。
ちなみにブラウザはEdgeとChrome両方使ってますがどちらも同じ状況です。
ほかにうまい方法があるでしょうか。
なお、Edgeはスリープタブという機能(Edgeの [設定] → [システム] → [リソースの節約])で使ってないタブのリソース消費を抑えることができる(The Marvellous Suspenderみたいな感じ)ため、この設定時間を短くしておくと、ローエンドPCで上記のようなフローを実行してタブだらけになってしまってもほぼパフォーマンスに影響なくフローが動き続けます。
根本的に解決してませんが、これはこれでなかなか凄いです。
空文字の作り方に悩んだ
文字列を操作するアクションが複数用意されているためこの辺りはかなりありがたいのですが、文字列の一部削除(空白への置換)に初めはてこずりました。
「変数の設定」で空の変数が指定できないのはまあ良いとして、「テキストを置換する」アクションで置換対象の文字列に空白を指定するとエラーになってしまうのでちょっと悩みます。
情報提供を頂いた空白文字列のハードコード%''%
を使うことで空の(ように動作する)文字列型変数を作ったり「テキストを置換する」で空文字に置換したりできましたが、ほかにもうまい方法があるかもしれません。
Text.Replace Text: Text TextToFind: Search IsRegEx: False IgnoreCase: False ReplaceWith: $'''%''%''' ActivateEscapeSequences: False Result=> Replaced
画像認識はうまい使い方に悩み中
画像認識の機能は自動化に頼もしいアクションになるかと思いましたが、現状確実に動作させるにはいろいろとコツが必要のようです。
この画像認識の動作をざっと検証してみると、指定した画像のスケールが異なるもの(高解像度の大きいもの、縮小されたもの)は判定できず、また彩度や明度が微妙に異なる場合も判定から外れてしまうようです。
Webページで出力される画像は元画像と異なるサイズで指定されていることも多く、スケーリング時の補完で微妙な違いが起きたり、罫線(border)やフォントはページの状態によってピクセル単位でのずれがあったりすることもあるせいか、Webページの一部などの文字や罫線でレイアウトされた要素をキャプチャ―して検出対象の画像にしてもなかなかうまく判定してくれません。
(フォントについてはWindowsのClearTypeによるサブピクセルも判定の邪魔をしているような?)
また、画像の判定は「許容値」を設定することでより判定をより緩やかにすることができるようなのですが、この判定の変化もどの程度効いているのかちょっとわかりにくく、なかなか正確に調整できませんでした。
また、画像判定処理のリソース負荷も高く、古いPCではこの許容値を上げると待ち時間が数倍に延びてしまいます。(これはしょうがないですね)
初めは複数の画像パターンを用意してどのような画像をどれくらい判定できるのか細かくブログでレポートしてみようかと思いましたが、実際にいくつか検証してみてもあまり明確な変化がなく、、、まとめられてません。
画像認識の適した利用方法になんとかたどり着きたいところですが、現状悩み中のポイントです。
Amazon APIの接続は現状無理?
Power Automate Desktopでは、「Webサービスを呼び出します」を使ってAPIを呼び出してデータを取得することが可能です。
ここではhttpヘッダーの指定も可能なため、ほぼあらゆるAPIに接続可能なのですが、Amazon(AWS)のAPI利用については現状の構造だとちょっと無理そうです。
Amazon API(AWS API)が使えない理由は、リクエストヘッダの完全な正規化ができない(たぶん)ため。
AWS APIでは、AWS署名バージョン4の署名付きリクエストを送信することで情報を取得することができます。Power Automate Desktopには暗号化キーを使った暗号化などにも対応しているので、AWS APIを使うための署名付きリクエストを作成するところまでは対応可能です。
そして、「Webサービスを呼び出します」のアクションではカスタムヘッダーが指定できるため、これを使うことで広い範囲でWebサービスにアクセスすることができるのですが、現状このアクションでは「受け入れる(Accept)」「コンテンツタイプ(Content-type)」の2つのヘッダに関しては設定が必須の項目になっています。
AWSではリクエストの際にhttpヘッダの正規化が必要となり、その際にリクエストヘッダをアルファベット小文字で統一し、各行をアルファベット順にソートしたものを使用する必要があるため、この部分を通過することができないと思われます。
(上記2つののヘッダは小文字で送信してない(多分)ほか、content-encoding:amz-1.0
などを間に入れることができない)
AcceptとContent-typeヘッダも含めてすべてカスタムヘッダで記述できれば完全に自由なAPIコントロールができそうなのですが、現状はここで検証が止まってしまっています。
ぶっちゃけ実際にAWS APIを使うなら提供されてるSDKや他の言語を経由してしまったほうが早いのですが、せっかくならPower Automate Desktopのアクションだけで完結してみたいところです。
もし他の方法などでAWS APIを利用する方法があれば知りたいです。
参考までに、以下はAWS呼び出しのための署名付きリクエストを作るフローのサンプルです。(動きません)
AWS APIを呼び出すPower Automate Desktopフロー(仮)
メインフロー
DateTime.Local DateTimeFormat: DateTime.DateTimeFormat.DateAndTime CurrentDateTime=> CurrentDateTime
Text.FromCustomDateTime DateTime: CurrentDateTime CustomFormat: $'''yyyyMMddThhmmssZ''' Result=> FormattedDateTime
Text.GetSubtextFromStartTo Text: FormattedDateTime NumberOfChars: 8 Subtext=> FormattedDate
CALL SetPayload
CALL SetCanonicalHeader
CALL CreateCanonicalRequest
CALL CreateStringToSign
CALL CalculateAwsSignatureV4
CALL AddSignatureToRequest
Text.AppendLine Text: RequestHeader LineToAppend: AuthorizationHeader Result=> RequestHeader
サブフロー:SetPayload
SET Payload TO $'''{}'''
Text.Replace Text: Payload TextToFind: $'''\\n''' IsRegEx: False IgnoreCase: False ReplaceWith: $'''\\n''' ActivateEscapeSequences: True Result=> PayloadMultiLined
Cryptography.HashText HashAlgorithm: Cryptography.HashAlgorithm.SHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: Payload HashedText=> HashedPayload
サブフロー:SetCanonicalHeader
# リクエストヘッダを作る
SET RequestHeader TO $'''content-encoding:amz-1.0'''
Text.AppendLine Text: RequestHeader LineToAppend: $'''content-type:application/json; charset=utf-8''' Result=> RequestHeader
Text.AppendLine Text: RequestHeader LineToAppend: $'''host:webservices.amazon.co.jp''' Result=> RequestHeader
Text.AppendLine Text: RequestHeader LineToAppend: $'''x-amz-date:%FormattedDateTime%''' Result=> RequestHeader
Text.AppendLine Text: RequestHeader LineToAppend: $'''x-amz-target:''' Result=> RequestHeader
# リクエストヘッダを正規ヘッダに変換
SET CanonicalHeader TO RequestHeader
Text.ChangeCase Text: CanonicalHeader NewCase: Text.CaseOption.LowerCase Result=> CanonicalHeader
サブフロー:CreateCanonicalRequest
# タスク 1: 署名バージョン 4 の正規リクエストを作成する
# HTTP リクエストメソッド (GET、PUT、POST など) を追加
SET CanonicalRequest TO RequestMethod
# 正規 URI パラメータを追加
Text.AppendLine Text: CanonicalRequest LineToAppend: CanonicalUriParams Result=> CanonicalRequest
# 正規クエリ文字列を追加
Text.AppendLine Text: CanonicalRequest LineToAppend: CanonicalQueryString Result=> CanonicalRequest
# 正規ヘッダーを追加
DISABLE Text.AppendLine Text: CanonicalRequest LineToAppend: CanonicalHeader Result=> CanonicalRequest
# 署名付きヘッダーを追加
Text.Split Text: CanonicalHeader StandardDelimiter: Text.StandardDelimiter.NewLine DelimiterTimes: 1 Result=> CanonicalHeaderList
Text.JoinWithCustomDelimiter List: CanonicalHeaderList CustomDelimiter: $''';''' Result=> SignedHeaders
# リクエストの本文のペイロードから作成したハッシュ値(HashedPayload)を追加
Text.AppendLine Text: CanonicalRequest LineToAppend: HashedPayload Result=> CanonicalRequest
# 正規リクエストのハッシュ(HashedCanonicalRequest)を作成
Cryptography.HashText HashAlgorithm: Cryptography.HashAlgorithm.SHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: CanonicalRequest HashedText=> HashedCanonicalRequest
サブフロー:CreateStringToSign
# タスク 2: 署名バージョン 4 の署名文字列(StringToSign)を作成する
# アルゴリズムを追加する
Text.AppendLine Text: StringToSign LineToAppend: Algorithm Result=> StringToSign
# 要求日付の値を追加する
Text.AppendLine Text: StringToSign LineToAppend: FormattedDateTime Result=> StringToSign
# 認証情報スコープの値を追加する
SET CredentialScope TO $'''%FormattedDate%/%AwsRegion%/%AwsService%/%AwsSigning%'''
Text.AppendLine Text: StringToSign LineToAppend: CredentialScope Result=> StringToSign
# タスク 1で作成した正規リクエストのハッシュ(HashedCanonicalRequest)を追加する
Text.AppendLine Text: StringToSign LineToAppend: HashedCanonicalRequest Result=> StringToSign
サブフロー:CalculateAwsSignatureV4
# タスク 3: AWS署名バージョン 4 の署名を計算する
# 署名キー(kSigning)を取得する
SET AwsSecretKey TO $'''AWS4%SecretKey%'''
Cryptography.HashTextWithKey HashAlgorithm: Cryptography.KeyedHashAlgorithm.HMACSHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: FormattedDate HashKey: AwsSecretKey HashedText=> kDate
Cryptography.HashTextWithKey HashAlgorithm: Cryptography.KeyedHashAlgorithm.HMACSHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: AwsRegion HashKey: kDate HashedText=> kRegion
Cryptography.HashTextWithKey HashAlgorithm: Cryptography.KeyedHashAlgorithm.HMACSHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: AwsService HashKey: kRegion HashedText=> kService
Cryptography.HashTextWithKey HashAlgorithm: Cryptography.KeyedHashAlgorithm.HMACSHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: AwsSigning HashKey: kService HashedText=> kSigning
# 署名を計算する
Cryptography.HashTextWithKey HashAlgorithm: Cryptography.KeyedHashAlgorithm.HMACSHA256 Encoding: Cryptography.EncryptionEncoding.Unicode TextToHash: StringToSign HashKey: kSigning HashedText=> Signature
サブフロー:AddSignatureToRequest
# Authorization ヘッダーに署名情報を追加する
SET AuthorizationHeader TO $'''Authorization: %Algorithm% Credential=%AccessKeyID%/%CredentialScope%, SignedHeaders=%SignedHeaders%, Signature=%Signature%'''
これでメインフローに署名済みのリクエストヘッダ%SignedHeaders%
が戻ってくるので、[カスタムヘッダ] に%SignedHeaders%
、[要求本文] に%PayLoad%
を指定すると!Amazon APIが!
動きませんでしたー。(←知ってた)
まとめ
今回のお話は基本的なフローの構築からは少し離れた話題のため、あまり影響のある方は少ないかと思いますが、ここ数日Power Automate Desktopを使ってみて気になったところを雑記としてまとめてみました。
いずれにしてもPower Automate Desktopはフローも組み立てやすくUIもシンプルなので、初めての方でも気軽に自動化チャレンジできる扱いやすいRPAツールだと思います。
クラベルでは初めてRPAにチャレンジする方に向けてテンプレートとして使えるPower Automate Desktopのフローサンプルを解説付きでご紹介していますのでぜひ参考にしてみてください。
お読みくださりありがとうございました。今回の内容が少しでもお役に立てば幸いです。
コメント