Playwrightをpythonで使ってみた

# Playwrightとは？
E2E自動テストの作成を助けてくれるフレームワーク
公式：https://playwright.dev/
githug：https://github.com/microsoft/playwright
TypeScriptやJavaScriptで扱うのが基本っぽい
これ以外でも当記事で扱うPythonの他、Javaや.Netもサポートしている

# インストール
### 参考
https://playwright.dev/python/docs/intro

### 環境
Windows10Pro
Python3.8.8

公式の通りにコマンドを打ち込んでいったら動いた ~~（から、公式を見た方が良い）~~
```console
# pytest(pythonのテストフレームワーク)のプラグインをインストール
pip install pytest-playwright

# 必要なブラウザのインストール
playwright install
```

これでインストールは完了

## 操作を記録する
ここではgoogleの検索を実行する操作を記録する
https://www.google.com/

操作を記録するコマンド
`Playwright codegen【対象ページのURL】-o【出力ファイルパス】`


```console
# 操作の記録をpythonのコードで現在のフォルダ配下に保存する
Playwright codegen https://www.google.com/ -o PlaywrightSample.py
```
これを実行するとブラウザが起動し、googleのページが表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174217/9bea8018-6943-41b9-359d-61ad9c3430b9.png)

検索ボックスに「今日の天気」を入力し、Enterを押下する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174217/4923b029-0673-fc36-f3a1-9fa9bc24a060.png)

検索結果が出てくることを確認して、ブラウザを閉じる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174217/946047f5-67fb-80b7-b6c7-e8edcfc4c5ee.png)

ここまでやって、コマンドプロンプト上の現在のフォルダを確認すると、pythonコードが出来上がっている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174217/33269ada-09e0-5779-5597-74a17457e0b1.png)

こんな感じのソースが出力される
```python:PlaywrightSample.py
from playwright.sync_api import Playwright, sync_playwright, expect


def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()

    # Open new page
    page = context.new_page()

    # Go to https://www.google.com/
    page.goto("https://www.google.com/")

    # Click text=ログインせずに使う
    page.frame_locator("iframe[role=\"presentation\"]").locator("text=ログインせずに使う").click()

    # Click [aria-label="検索"]
    page.locator("[aria-label=\"検索\"]").click()

    # Fill [aria-label="検索"]
    page.locator("[aria-label=\"検索\"]").fill("今日の天気")

    # Click [aria-label="検索"]
    page.locator("[aria-label=\"検索\"]").click()

    # Press Enter
    page.locator("[aria-label=\"検索\"]").press("Enter")
    page.wait_for_url("https://www.google.com/search?q=%E4%BB%8A%E6%97%A5%E3%81%AE%E5%A4%A9%E6%B0%97&source=hp&ei=pJoEY-CECavs2roP9IGkqA0&iflsig=AJiK0e8AAAAAYwSotFt-9IhNDjLm4nMhtp0zg_Urj1OT&ved=0ahUKEwjg5oyF0Nz5AhUrtlYBHfQACdUQ4dUDCAk&uact=5&oq=%E4%BB%8A%E6%97%A5%E3%81%AE%E5%A4%A9%E6%B0%97&gs_lcp=Cgdnd3Mtd2l6EAMyDQgAEIAEEMkDEEYQgAIyBQgAEJIDMgUIABCSAzIFCAAQgAQyCAgAELEDEIMBMgUIABCABDIFCAAQgAQyCwgAEIAEELEDELEDMggIABCxAxCDATIFCAAQgAQ6CAgAEI8BEOoCOgYIABAEEAM6DQgAEIAEELEDEIMBEAQ6BwgAEIAEEAQ6CggAEIAEELEDEAQ6EAgAEIAEELEDEIMBEMkDEAQ6FQgAEIAEELEDEIMBEMkDEAQQRhCAAjoSCAAQgAQQsQMQgwEQBBBGEIACOg8IABCABBDJAxAEEEYQgAI6GQgAEIAEELEDEIMBELEDEIMBEMkDEEYQgAI6EQgAEIAEELEDEIMBELEDEIMBOh0IABCABBCxAxCDARCxAxCDARDJAxAEECUQRhCAAjoVCAAQgAQQsQMQgwEQsQMQgwEQBBAlOgkIABCABBAEECU6CAgAEAQQAxAlOgwIABCABBCxAxAEECU6FwgAEIAEELEDEIMBEMkDEAQQJRBGEIACOg8IABCABBCxAxCDARAEECVQ9wpYqz1guewBaAlwAHgAgAH5AYgByRGSAQYxMS43LjKYAQCgAQGwAQo&sclient=gws-wiz")

    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

```
当たり前だけど、ログイン確認やいくつかのクリックなど、目的の操作とは直接関係のないものが入り込んでいるので、整理する

また、このまま実行してしまうとpage.wait_for_url()のところでタイムアウトエラーになる
根本的な原因がはっきりしないが、今は動かすことを優先し、代替えの処理に差し替える
page.wait_for_selector()を使って、特定のイメージ（今回はgoogleのロゴ）が表示するまで待つようにしてみた

```python:PlaywrightSample.py
from playwright.sync_api import Playwright, sync_playwright, expect


def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()

    # Open new page
    page = context.new_page()

    # Go to https://www.google.com/
    page.goto("https://www.google.com/")

    # Fill [aria-label="検索"]
    page.locator("[aria-label=\"検索\"]").fill("今日の天気")

    # Press Enter
    page.locator("[aria-label=\"検索\"]").press("Enter")
    page.wait_for_selector("img[alt=\"Google\"]")
    #page.wait_for_url(url="https://www.google.com/search?q=%E4%BB%8A%E6%97%A5%E3%81%AE%E5%A4%A9%E6%B0%97&source=hp&ei=pJoEY-CECavs2roP9IGkqA0&iflsig=AJiK0e8AAAAAYwSotFt-9IhNDjLm4nMhtp0zg_Urj1OT&ved=0ahUKEwjg5oyF0Nz5AhUrtlYBHfQACdUQ4dUDCAk&uact=5&oq=%E4%BB%8A%E6%97%A5%E3%81%AE%E5%A4%A9%E6%B0%97&gs_lcp=Cgdnd3Mtd2l6EAMyDQgAEIAEEMkDEEYQgAIyBQgAEJIDMgUIABCSAzIFCAAQgAQyCAgAELEDEIMBMgUIABCABDIFCAAQgAQyCwgAEIAEELEDELEDMggIABCxAxCDATIFCAAQgAQ6CAgAEI8BEOoCOgYIABAEEAM6DQgAEIAEELEDEIMBEAQ6BwgAEIAEEAQ6CggAEIAEELEDEAQ6EAgAEIAEELEDEIMBEMkDEAQ6FQgAEIAEELEDEIMBEMkDEAQQRhCAAjoSCAAQgAQQsQMQgwEQBBBGEIACOg8IABCABBDJAxAEEEYQgAI6GQgAEIAEELEDEIMBELEDEIMBEMkDEEYQgAI6EQgAEIAEELEDEIMBELEDEIMBOh0IABCABBCxAxCDARCxAxCDARDJAxAEECUQRhCAAjoVCAAQgAQQsQMQgwEQsQMQgwEQBBAlOgkIABCABBAEECU6CAgAEAQQAxAlOgwIABCABBCxAxAEECU6FwgAEIAEELEDEIMBEMkDEAQQJRBGEIACOg8IABCABBCxAxCDARAEECVQ9wpYqz1guewBaAlwAHgAgAH5AYgByRGSAQYxMS43LjKYAQCgAQGwAQo&sclient=gws-wiz",wait_until='networkidle')

    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)


```
コメントが操作単位に入っているので、初見でも触りやすい
構造が地獄なページを触った場合のlocatorがどうなるかも確認してみたいところ


## 記録した操作を実行する

pyやpytestで出力したソースを実行するだけ
```console
# 単純なpythonコードとして実行する場合
py .\PlaywrightSample.py

# テストとして実行する場合
pytest .\PlaywrightSample.py
```

## 感想

出力されたコードが初見でもわかりやすいことに感動した
先にも触れたが、構造が地獄なページだとどうなるのかが興味ある
セレクターは研究が必要かもしれない
page.wait_for_url()のタイムアウトエラーについて調査していたところ、デフォルトタイムアウトの設定が私の環境では効かないことがわかった（使い方が悪かった？）

使い込んでいけば色々こうしてほしい点は出てくるだろうが、seleniumのようにイチからコードを組むことに敷居の高さを感じる人には良いと思う