[[Lightpanda]]というものを知ったので、[[Playwright]]で[[Chromium]]とどれくらい差が出るかを試してみた。結論から言うと[[Playwright]]にはまだ対応されていなそうで動かなかった。
## プロジェクト作成
[[Playwright]]のプロジェクトを[[toki]]で作成する。
```console
toki playwright lightpanda-sandbox
```
## テストコードを書く
[[Minerva]]にアクセスするテストケースを書いてみる。
```ts
import { type Locator, type Page, expect, test } from "@playwright/test";
async function カードリンクをクリック(page: Page, title: string) {
await test.step(
`カードリンクをクリック "${title}"`,
async () => {
await page.locator(`[data-href="${title}"]`).click();
},
{ box: true },
);
}
async function backlinkが表示されていること(page: Page) {
await test.step(
"backlinkが表示されていること",
async () =>
await expect(
page.getByRole("link", {
name: "📰ググってヒットしたNeovimプラグイン数",
exact: true,
}),
).toBeVisible(),
{ box: true },
);
}
async function n番目のリンク先に移動して1段落目の文章を取得する(
page: Page,
index: number,
links: Locator,
) {
await test.step(
`${index + 1}番目のリンク`,
async () => {
await test.step("移動", async () => {
await backlinkが表示されていること(page);
await links.nth(index).click();
await expect(page.getByText("作成:20")).toBeVisible();
});
await test.step("1段落目の文章取得", async () => {
const text = await page.locator("p").first().textContent();
console.log(text);
});
await test.step("戻る", async () => {
await page.goBack();
});
},
{ box: true },
);
}
test("Minerva test case", async ({ page }) => {
await page.goto("https://minerva.mamansoft.net/Home");
await test.step(
"Vim駅伝のページまで移動",
async () => {
await カードリンクをクリック(page, "📒Articles");
await カードリンクをクリック(page, "📒2024 Articles");
await カードリンクをクリック(page, "📘2024年 Neovim成長日記");
await page
.getByRole("link", { name: "Vim駅伝", exact: true })
.first()
.click();
await backlinkが表示されていること(page);
},
{ box: true },
);
await test.step("Weekly Report以外のbacklink先に移動し、1段落目の文章を取得", async () => {
const links = page
.locator(".backlinks")
.getByRole("link")
.filter({ hasNotText: "Weekly Report" });
const linkNum = await links.count();
for (let i = 0; i < linkNum; i++) {
await n番目のリンク先に移動して1段落目の文章を取得する(page, i, links);
}
});
});
```
## Chromiumで実行
以下の条件でそれぞれ3回実行。
| 条件 | 1回目 | 2回目 | 3回目 |
| -------- | ----- | ----- | ----- |
| headed | 11.3s | 10.5s | 10.8s |
| headless | 10.9s | 9.1s | 9.1s |
流石にheadlessの方が少し速い。
## Lightpandaのインストール
[[Lightpanda]]をインストールする。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
GitHub - lightpanda-io/browser: Lightpanda: the headless browser designed for AI and automation
</div>
<div class="link-card-v2-content">
Lightpanda: the headless browser designed for AI and automation - lightpanda-io/browser
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/8c1ad9756b278b561363a16cfe2b0112c6b277acd4e0df6ce27be506ce63313e/lightpanda-io/browser" />
<a href="https://github.com/lightpanda-io/browser"></a>
</div>
```console
$ wget https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux
$ chmod a+x ./lightpanda-x86_64-linux
```
```console
./lightpanda-x86_64-linux --host 127.0.0.1 --port 9222
```
## テストを実行してみたがPlaywrightには未対応ぽい
testを以下のように `ws://localhost:9222` を使うように書き換えてみた。
```ts
test("Minerva test case", async ({ playwright }) => {
const browser = await playwright.chromium.connectOverCDP(
"ws://localhost:9222",
);
const context = await browser.newContext();
const page = await context.newPage();
await test.step("Minervaのトップページに移動", async () => {
await page.goto("https://minerva.mamansoft.net/Home");
});
await test.step(
"Vim駅伝のページまで移動",
async () => {
await カードリンクをクリック(page, "📒Articles");
await カードリンクをクリック(page, "📒2024 Articles");
await カードリンクをクリック(page, "📘2024年 Neovim成長日記");
await page
.getByRole("link", { name: "Vim駅伝", exact: true })
.first()
.click();
await backlinkが表示されていること(page);
},
{ box: true },
);
await test.step("Weekly Report以外のbacklink先に移動し、1段落目の文章を取得", async () => {
const links = page
.locator(".backlinks")
.getByRole("link")
.filter({ hasNotText: "Weekly Report" });
const linkNum = await links.count();
for (let i = 0; i < linkNum; i++) {
await n番目のリンク先に移動して1段落目の文章を取得する(page, i, links);
}
});
});
```
しかしエラーになる。
```console
$ pnpm exec playwright test
Running 1 test using 1 worker
1) [chromium] › tests/example.spec.ts:54:5 › Minerva test case ───────────────────────────────────
Error: Assertion error
Error: Assertion error
Failed worker ran 1 test:
[chromium] › tests/example.spec.ts:54:5 › Minerva test case
1 failed
[chromium] › tests/example.spec.ts:54:5 › Minerva test case ────────────────────────────────────
1 error was not a part of any test, see above for details
Serving HTML report at http://localhost:9323. Press Ctrl+C to quit.
```
該当箇所。
```
54 | test("Minerva test case", async ({ playwright }) => {
55 | const browser = await playwright.chromium.connectOverCDP(
| ^
56 | "ws://localhost:9222",
```
[[ChatGPT]]で[[o3-mini-high]]に聞いてみたところ、[[Lightpanda]]の `/json/version` から返却される[[JSON]]が[[Playwright]]のチェックに引っかかっているのではないかとのこと。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://cdn.oaistatic.com/assets/favicon-o20kmmos.svg" />
<span class="link-card-v2-site-name">ChatGPT</span>
</div>
<div class="link-card-v2-title">
ChatGPT - Lightpanda Browser Playwright エラー
</div>
<div class="link-card-v2-content">
Shared via ChatGPT
</div>
<img class="link-card-v2-image" src="https://cdn.oaistatic.com/assets/chatgpt-share-og-u7j5uyao.webp" />
<a href="https://chatgpt.com/share/679f3f9b-f27c-800a-b579-eedd75b85e09"></a>
</div>
```console
$ curl "http://localhost:9222/json/version"
{"webSocketDebuggerUrl": "ws://127.0.0.1:9222/"}
```
加えて、[[Playwright]]では `assertion error` になって動かない旨のissueもある。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
Playwright CDP connection fails with assertion error · Issue #363 · lightpanda-io/browser
</div>
<div class="link-card-v2-content">
When connecting to browser using Playwright's Python CDP interface, connection is immediately closed with an ass ...
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/a2045828fd691f4b7f697afbd9fd106c425a6a83239b27c848b77c34f3a14c02/lightpanda-io/browser/issues/363" />
<a href="https://github.com/lightpanda-io/browser/issues/363"></a>
</div>