インターフェース上級¶
この章では、より高度な自動化インターフェースを紹介します。これらのインターフェースを通じて、さまざまな細かい操作を実行できます。本章は内容が多いため、初めて触れる場合は、各セクションをじっくり読むことをお勧めします。
豆知識
自動化コードを作成する際は、リモートデスクトップの右側の端末にコマンド 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(timeout=0) # ミリ秒
要素が存在する場合はクリックし、要素が存在しない場合もこのインターフェースは例外を発生させません。このインターフェースも同様に 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
テキスト入力¶
テキスト入力は注意が必要な点です。ボタンにテキストを入力することはできません。それはボタンだからです。ここでは入力欄要素を改めて選択して説明します。この要素の基本情報は以下のとおりです。

注目
入力欄要素を取得する際の注意点があります。入力欄要素を取得するときは、入力メソッド(IME)が表示された状態である必要があります。その状態で関連要素を検索してください。また、注意深く検索することをお勧めします。そうしないと、実際の入力欄ではない要素を取得してしまう可能性があります。
ヒント
自動化フローで入力メソッドを表示状態にするには、コード内で先に親コンテナに表示されている入力欄をクリックするだけで済みます。
上記の入力欄には、次のインターフェースを呼び出して「你好世界」という文字列を入力できます。英語やその他の 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
| 方向指定 | 説明 |
|---|---|
| Direction.DIR_UP | 上へスワイプ |
| Direction.DIR_LEFT | 左へスワイプ |
| Direction.DIR_DOWN | 下へスワイプ |
| Direction.DIR_RIGHT | 右へスワイプ |
フリック¶
フリックは、人が素早くスワイプする動作に似ています。この操作は画面を高速でスワイプし、素早く閲覧する操作のシミュレーションに適しています。以下の例では画面を上から下へフリックします。例ではセレクターが空ですが、実際の状況に応じてセレクターを指定するかどうかを選ぶ必要があります。
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") の一文だけで済みます。ただし、テキストでどうしても特定できない場合を除きます。
等速スクロール¶
固定ステップ長 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) # 左へ