Playwrightでスクレイピングを実践してみて得た気づき
はじめに
初めまして、株式会社 Rehab for JAPAN にてオンラインリハビリサービスRehab Studioの開発チームに所属している徳永です。
今回は、ここ数ヶ月ほど前にスクレイピングのタスクを依頼されて実施しておりました。
今回は上記のタスクを実践して分かった Playwright の tips を共有したいと思います。
また、掲載しているコードの一部は実際のコードを元に記載していますが、一部説明のために改変しています。
もし、本記事を参考に実装される際には、適宜環境に合わせてコードを修正してください。
対象読者 (ターゲット)
- Playwright を使ったことがないが、興味がある人。
Playwright とは
E2E テストを自動化するためのツールです。
似たようなツールとしては、Selenium などがあるそうです。
また、Playwright についてはライブラリがいくつかのプログラミング言語で提供されており、 TypeScript, Python, Java, .Net で記述することができます。
私は普段業務で利用している TypeScript をよく使っています。
ちなみに Playwright は公式ドキュメントが充実しているので、詳細に知りたい方は公式ドキュメントを参照いただくのが良いかと思います。
おすすめな使い方
私は、先に codegen という Playwright でブラウザの操作を記録してコードを生成するツールを使って、
自動化の流れを理解しました。
詳しい説明はこちらのブログに記載されてました。
初めて利用される際には、codegen を使ってイメージ掴んでもらうのが良さそうかと思います。
// temrminalから以下のコマンドを実行することで、codegenを使うためのブラウザが立ち上がります。
npx playwright codegen https://google.com
// 以下のコードはcodegenで生成されたコードです。
import { test, expect } from "@playwright/test";
test("test", async ({ page }) => {
await page.goto("https://www.google.com/");
await page.getByLabel("検索", { exact: true }).click();
await page.getByLabel("検索", { exact: true }).fill("playwright");
});
ちなみに codegen を使った際に、コード生成用のブラウザと同時に次のような window が立ち上がります。
言語を切り替えると、その言語に合わせたコードが生成されます。
試しに Target を Java に設定すると以下のようなコードが生成されます。
import com.microsoft.playwright.junit.UsePlaywright;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.*;
import org.junit.jupiter.api.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.*;
@UsePlaywright
public class TestExample {
@Test
void test(Page page) {
page.navigate("https://www.google.com/");
page.getByLabel("検索", new Page.GetByLabelOptions().setExact(true)).click();
}
}
また、↓ の図のようにコード生成用のブラウザにカーソルを合わせると、
その要素に対応するコードがモーダルで表示されるので、表示されているモーダルなどのコードを参考に実装もできます。
実装例と tips
認証するページがある場合
ログインが必要なページの場合には、通常と同様にログインページに遷移してログイン処理を行います。
パスワード、ユーザー名を入力してログインボタンをクリックする処理は以下のようになりました。
.env に、ログインユーザ、パスワードを設定する想定で記述しています。
await page.goto("https://XXXXX.jp/login");
const MAIL: string = process.env.MAIL || "";
const PASSWORD: string = process.env.PASSWORD || "";
if (MAIL === "" || PASSWORD === "") {
//unexpected error
console.log("unexpected error. please check .env file.");
return;
} else {
await page.getByPlaceholder("メールアドレス").fill(MAIL);
await page.getByPlaceholder("パスワード").click();
await page.getByPlaceholder("パスワード").fill(PASSWORD);
await page.locator("#form div").filter({ hasText: "ログイン" }).click();
}
テーブルの表示を waitForSelector で待ったのちにデータを取得する
ある要素の表示が完了するまで waitForSelector で待ったのちにデータを取得することで、テーブルのデータを取得することができるようになります。
もともと、waitForSelector を入れずにデータを取得しようとしていたのですが、それだとうまくデータを取得できないことがあり導入しました。
こちらはベストプラクティスの記事など参考に実装しました。
// selector が表示されるまで待つ
await page.waitForSelector("tbody.table-body.border-bottom.list");
// tableの要素を取得()
await page.$$eval(
"table-selector tr", // ここでテーブルの行要素を指定します
(rows) => {
const cols = [];
const numCols = rows[0].children.length; // 各行のセルの数を取得
for (let i = 0; i < numCols; i++) {
cols.push(
Array.from(rows, (row) => {
const cell = row.children[i]; // 各セルを取得
console.log(`${cell}`); // セルの内容をコンソールに出力
return cell ? cell.innerText.trim().replace(/\r?\n/g, "") : ""; // セルのテキストを整形して配列に追加
})
);
}
return cols;
}
);
(非推奨プラクティス) タイムアウトを設定する
基本的には、一つ前のセクションで記載したように、要素が表示されるまで待つことが Playwright のベストプラクティスとされています。
例えばセレクトボックスを選択して特定の月を選択した際に、テーブルが再描画されるデータを取得する際に想定した特定の月のデータを取得できない場合がありました。
現状の解決方法としてはページ遷移を待つことで解決していますが、Playwright のベストプラクティスとしては非推奨とされています。
// selectorの選択
await page.selectOption("select#month", "2024-10");
await page.waitForTimeout(3000);
そのほか Playwright を実施するにあたっての便利ツール
VS Code 用のプラグインが提供されているので、Playwright を使う際にはインストールしておくと便利です。
処理時間やテストの成功・失敗なども分かりやすく表示されます。
まとめ
Playwright を使ってスクレイピングを実施してみて、いくつかの tips を共有しました。
実際には E2E テストを自動化するためのツールとして使われることが多いですが、スクレイピングにも使えることが分かりました。
一方で、安定的に動かそうとすると難しい部分も存在することを改めて実感しました。
まだまだスクリーンショットを取ったりと便利な機能があるようなので、今後も Playwright を使っていくことで、より便利な機能を使いこなせるようになりたいと思います。
Discussion