インターフェイスの応用¶
本章では、より高度な自動化インターフェースを紹介します。これらのインターフェースを使用することで、さまざまな細かい操作を実行できます。本章は内容が豊富ですので、初めての方は各セクションを最後までじっくりと読むことをお勧めします。
豆知識
自動化コードを作成する際は、リモートデスクトップの右側のターミナルで lamda コマンドを入力し、その中で以下のテストコードを実行するか、または自分で要素の選択やクリックテストなどを行うことで、作成と検証の速度を上げることができます。
要素の取得¶
基礎知識の章や前の部分で既にご存知かもしれません。操作を行うには、セレクターを使用して関連する要素を見つける必要があります。セレクターのパラメータをどこで取得できるかもご覧になったことでしょう。これからの説明はこの要素を中心に展開します。この画像の右側に「同意」要素に関する情報が表示されています。

注目
左側のインターフェースで直接クリックした要素が実際の要素とは限りません。他の要素とサイズや位置が重なっている可能性があるためです。通常、位置やサイズが重複している要素は右側の情報バーに表示されますので、上下にスクロールして本当に必要なものを確認できます。また、左側のインターフェースで TAB キーを押して手動ですべての要素を巡回することでも確認できます。
上記の要素に対しては、一般的に text を使って取得します。text を使用する条件は、現在のインターフェースに他に「同意」という text を持つ要素が存在しないことです。これが最も簡単な方法です。次に、resourceId を使用することもできます。ただし、ここでの resourceId は一意の ID ではなく、リソース ID を表しており、1つのインターフェース内に同じリソース ID を持つ要素が多数存在する可能性があることに注意してください。packageName や checkable などの他のフィールドは通常あまり使用されませんが、text、resourceId、description のいずれも利用できない場合は、これらのフィールドを試すことができます。以下のようないくつかの方法でこの要素を取得できます。
element = d(text="同意")
element = d(text="同意", resourceId="com.tencent.news:id/btm_first_agree")
element = d(resourceId="com.tencent.news:id/btm_first_agree")
要素のクリック¶
以下のインターフェースを呼び出して通常の要素クリック操作を行うと、文脈上「同意」を手動でクリックしたのと同じ効果が得られます。
element.click()
要素の特定の位置をクリックする必要がある場合は、クリックインターフェースの呼び出し時に corner パラメータを指定できます。たとえば、Corner.COR_CENTER は要素の中心点をクリックすることを示し、左上隅または右下隅(Corner.COR_BOTTOMRIGHT)をクリックすることもできます。
element.click_exists(corner=Corner.COR_TOPLEFT)
この要素に対して長押し操作を実行します。存在しない場合は例外がスローされます。このインターフェースも corner をサポートしていますが、長押し時間を指定することはできません。
element.long_click()
要素が存在する場合にクリックします。要素が存在しない場合、このインターフェースを呼び出しても例外は発生しません。このインターフェースも同様に corner をサポートしています。
element.click_exists()
>>> element.click_exists()
True
存在確認¶
多くの場合、次のステップに進む前に要素の存在を確認する必要があります。そうしないと、後続のフローで例外が発生したり、誤ったインターフェースで誤った操作を実行したりする可能性があります。その場合は、以下のインターフェースを使用して存在を判断できます。
element.exists()
要素情報¶
場合によっては、要素の座標や領域情報、要素上のテキストや説明などの文字列情報など、要素の一部の情報を取得したいことがあります。以下のインターフェースを使用して要素情報を読み取ることができます。
element.info()
上記のテスト要素の場合、出力される情報は次のようになります。
>>> info = element.info()
>>> print (info)
bounds { ... }
className: "android.widget.TextView"
clickable: true
enabled: true
focusable: true
packageName: "com.tencent.news"
resourceName: "com.tencent.news:id/btn_first_agree"
text: "\345\220\214\346\204\217"
visibleBounds { ... }
ヒント
上記で出力された情報に description などのいくつかのフィールドが欠落していることに気付くかもしれません。このような場合、通常はそのフィールドの値が空または false であることを示します。それでも、通常通り属性にアクセスして関連するフィールドの値を取得できます。
この情報はやや複雑で、protobuf のデフォルトの表示形式です。対応する属性に直接アクセスすることで、実際の値を出力できます。たとえば、要素の text の値を読み取りたい場合は、次のように使用します。
>>> info = element.info()
>>> print (info.text)
同意
もちろん、要素の領域座標に関連する情報も含まれており、それらにもアクセスできます。たとえば、この要素に対応する領域情報を取得したい場合、以下のように領域情報を出力したり、後続の操作で使用するために変数に保存したりできます。
>>> info = element.info()
>>> print (info.bounds)
top: 947
left: 338
bottom: 997
right: 743
出力または返される値は領域情報(Bounds)であり、一部のスクリーンショットインターフェースで使用されるパラメータでもあることがわかります。このパラメータをスクリーンショットインターフェースに渡すことで、要素単独のスクリーンショットを撮ることができます。ただし、より簡単な方法がすでに用意されています。
要素の幅と高さを取得してオフセットを計算したい場合もあります。たとえば、他の要素との相対的なオフセットを計算する場合です。次のように使用します。
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
または、要素の中心点や角点(左上隅、右下隅など)を取得します。以下のインターフェースは通常 Point オブジェクトを返し、Point オブジェクトから対応する X および Y のデバイス画面座標を取得することもできます。
>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792
以下の呼び出しは要素の角点座標を取得するために使用されます。例では要素の左上隅の座標を取得しています。これ以外にも、bottom-right、top-right、bottom-left の合計4つの隅の座標を取得できます。
>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550
要素の反復処理¶
セレクターで選択されたすべての要素を反復処理することもできます。通常、現在のコンテキストではこのセレクターは1つの要素のみに一致する可能性があります。反復処理をテストする場合は、複数の要素に一致するセレクターを選択してください。セレクター上で直接 for ループやその他の方法を使用して反復処理できます。
for i in element: print (i.info())
または、複数の一致要素が存在することがわかっていて、特定の N 番目の一致要素を取得したい場合は、次の方法で取得できます。
element_3rd = element.get(3)
要素のカウント¶
通常、このインターフェースを直接使用することはありません。以下の呼び出しで、現在のセレクターに一致する要素の数を取得できます。
>>> element.count()
1
要素のスクリーンショット¶
要素レベルのスクリーンショットをサポートしており、全画面のスクリーンショットを撮ってから切り取ることなく、要素の画像を個別にキャプチャできます。
element.screenshot(quality=60)
スクリーンショットを撮った後、getvalue() メソッドを使用して直接バイナリデータを取得したり、PIL.Image に渡したりできます。
>>> element.screenshot(quality=60).getvalue()
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xe2\x02(ICC_PROFILE\x00\x01\x01\x00\x00\x02\x18\x00\x00\x00\x00\x02\x10\x00\x00mntrRGB XYZ \x00\x00...'
または、さらに処理する必要がない場合は、スクリーンショットを直接ローカルファイルに保存することもできます。
>>> element.screenshot(quality=60).save("image.png")
要素の待機¶
場合によっては、現在のページが読み込み完了したかどうかを判断する必要があります。通常、関連する要素が出現したかどうかを判断することで、ページの読み込みが完了したかどうかを判断できます。以下の例では、「同意」要素が出現するまで最大10秒間待機します。
ヒント
ここでの待機時間はミリ秒単位なので、10秒は *1000 して、10秒 = 10000 ミリ秒となります。
element.wait_for_exists(10*1000)
>>> element.wait_for_exists(10*1000)
True
さらに、要素が消えるのを待つ、つまり要素がインターフェースから消えるのを待つこともサポートしています。
element.wait_until_gone(10*1000)
>>> element.wait_until_gone(10*1000)
False
テキスト入力¶
テキスト入力は注意が必要な点です。ボタンにテキストを入力することはできません。それはボタンだからです。ここで、新しい入力ボックス要素を選択して紹介します。この要素の基本情報は次のとおりです。

注目
入力ボックス要素を取得する際の注意点:入力ボックス要素を取得する際は、必ず入力メソッドがポップアップ表示された状態にしてから関連要素を検索してください。また、よく探すことをお勧めします。そうしないと、取得したものが実際の入力ボックスでない可能性があります。
ヒント
自動化フローにおいて、入力メソッドをポップアップ状態にするには、コード内で先行して親コンテナに表示されている入力ボックスをクリックするだけで十分です。
上記の入力ボックスに対して、以下のインターフェースを呼び出して「你好世界」という文字列を入力できます。英語やその他の Unicode 文字列も入力可能で、以下のように使用するだけでボックス内にテキストを入力できます。
>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True
この入力ボックスに現在表示されているテキスト内容を取得したい場合は、次のように呼び出します。
注目
ここでセレクターを変更していることに注意してください。初期のセレクターは text 属性を使用していましたが、テキスト入力後に要素の内容が変更され、元のセレクターで一致しなくなったため、別のセレクターに変更しました。適切なセレクターを選択することは重要ですが、この例はあくまでデモンストレーションですので、そのようにしています。
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'
現在入力されている内容をクリアすることもできます。通常、テキスト入力時には自動的に元のテキストが消去されますが、手動で消去することも可能です。
ヒント
キーインターフェースを連続して使用し、BACKSPACE キーをループで押下しても同様の効果を得られます。
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True
注釈
極端なケースでは、このインターフェースを使用してテキストを正常に入力できない箇所もあります。対応中です。
通常のスワイプ¶
以下のインターフェースを使用して、画面のスワイプ操作(リストの上下スクロールなど)を行います。以下の呼び出しは上方向へのスワイプを実装します。step の値が大きいほど、スワイプ速度は遅くなり、精度の高いスワイプが必要な場合に適しています。
注目
単純なケースでは、この操作にセレクターパラメータを指定する必要はありません。スワイプできない場合は、scrollable 属性を持つ要素やリストの第一階層コンテナなど、適切な要素にセレクター条件を設定してください。
d().swipe(direction=Direction.DIR_UP, step=32)
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.swipe(direction=Direction.DIR_UP, step=32)
True
フリック(高速スワイプ)¶
フリックは人間の高速スワイプ動作に似ており、この操作は画面を素早くスライドさせます。素早くブラウジングするような操作のシミュレーションに適しています。以下の例では、画面を上から下にスワイプします。例ではセレクターが空ですが、実際の状況に応じてセレクターを指定するかどうかを選択する必要があります。
d().fling_from_top_to_bottom()
下から上への高速スワイプ:
d().fling_from_bottom_to_top()
左から右への高速スワイプ:
d().fling_from_left_to_right()
右から左への高速スワイプ:
d().fling_from_right_to_left()
注目
単純なケースでは、この操作にセレクターパラメータを指定する必要はありません。スワイプできない場合は、scrollable 属性を持つ要素やリストの第一階層コンテナなど、適切な要素にセレクター条件を設定してください。
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True
要素のドラッグ¶
要素を別の要素の位置までドラッグします(例:アプリアイコンをフォルダにドラッグ)。
element.drag_to(Selector(text="购物")) # ターゲット要素の位置にドラッグ
子要素・兄弟要素のクエリ¶
繰り返し要素や明確な特徴のない要素に対しては、まず親コンテナを特定し、次に child で子要素を、sibling で兄弟要素を取得して範囲を絞り込むことができます。
form = d(resourceId="login_form") # 親コンテナを特定
form.child().get(1) # form 下の最初の子要素を取得
form.sibling(textContains="找回密码") # form と同じ階層の要素で、テキストに "找回密码" を含むものを取得
# 以下はやや複雑なクエリの例です
# resourceId=com.example.com:id/resource に一致する最初の結果を取得し、その resourceId=com.example.com:id/abc の子ノードを選択し、さらにその子ノード下で description に "一天内" を含む要素をクエリし、その情報を出力します。
d(resourceId="com.example.com:id/resource").get(0).child(resourceId="com.example.com:id/abc").child(descriptionContains="一天内").info()
以下のサンプルレイアウト情報に対しては、以下のクエリ方法でその要素を正確に選択できます。
d(resourceId="com.zhiliaoapp.musically:id/bxa").child().get(3).child().get(1).info()
ヒント
ほとんどの場合、このように精密な child sibling クエリを使用する必要はなく、単に d(text="Continue with Google") だけで済みます。テキストによる特定がどうしてもできない場合を除きます。
境界までのフリック¶
最大スワイプ回数に達するまで高速スワイプを続けます。必ずしも最下部に到達したことを検出できるとは限らないため、max_swipes を指定する必要があります。
d().fling_from_top_to_bottom_to_end(max_swipes=32) # 下方向に境界までフリック
d().fling_from_bottom_to_top_to_end(max_swipes=32) # 上方向
d().fling_from_left_to_right_to_end(max_swipes=32) # 右方向
d().fling_from_right_to_left_to_end(max_swipes=32) # 左方向
等速スクロール¶
固定ステップ step でスワイプします。swipe よりも機械的で、安定したステップ進行が必要な場面に適しています。
d().scroll_from_top_to_bottom(step=60) # 下方向
d().scroll_from_bottom_to_top(step=60) # 上方向
d().scroll_from_left_to_right(step=60) # 右方向
d().scroll_from_right_to_left(step=60) # 左方向
境界までの等速スクロール¶
境界までの高速スワイプと同様ですが、等速スクロールで fling の代わりに行います。同じく max_swipes と step を指定する必要があります。
d().scroll_from_top_to_bottom_to_end(max_swipes=32, step=60) # 下方向に境界までスクロール
d().scroll_from_bottom_to_top_to_end(max_swipes=32, step=60) # 上方向
d().scroll_from_left_to_right_to_end(max_swipes=32, step=60) # 右方向
d().scroll_from_right_to_left_to_end(max_swipes=32, step=60) # 左方向