
Playwrightでtableの値を取得
Playwrightを使用していると、tableのtd内の値を取得する機会が多いですが、このコードを正しく書けているケースは少ないです。
悪い例だと、tableの各行をループを使わずに取得しているケースがあり、保守性や再利用性の低いコードが多く存在します。
tableのtdの値は3行のコードで取得可能
Playwrightでtableのtdの値の取得に無駄に長いコードを書いている方をよく見かけますが、tableのtdの値は3行のコードで取得可能です。
この記事では、以下の天気サイトの週間天気のtableのtdの値を取得する方法を交えて説明します。
まず、以下のコマンドで my-tenki のディレクトリを作成します。
mkdir my-tenki; cd my-tenki;
次にPlaywrightを以下のコマンドでインストールします。
npm init playwright@latest
npx playwright test を実行するとexample.spec.tsに書かれているコードのテストが実行されます。
今回は東京(東京)の天気のWebページの週間天気のtableのtdの値を取得するので、example.spec.tsを以下のように書き換えて、gotoで遷移するようにします。
import { test, expect } from '@playwright/test';
test('週間天気テスト', async ({ page }) => {
await page.goto('https://weather.yahoo.co.jp/weather/jp/13/4410.html');
});
次に週間天気のページの table (.yjw_table)のtrをpage.locatorで指定して、all() で各行のlocatorをtrsに配列で代入します。
import { test, expect } from '@playwright/test';
test('週間天気テスト', async ({ page }) => {
await page.goto('https://weather.yahoo.co.jp/weather/jp/13/4410.html');
const trs = await page.locator('.yjw_table tr').all();
console.log(trs);
// [
// locator('.yjw_table tr').first(),
// locator('.yjw_table tr').nth(1),
// locator('.yjw_table tr').nth(2),
// locator('.yjw_table tr').nth(3)
// ]
});
次にtr内のテキストをallInnerTexts()で取得して、join('').split(/\s+/) で配列化します。
tdタグ内の値の取得なので、page.locatorで「td」を指定されている方が多いですが、trからテキストを取得して配列化したほうが素早く処理できます。
import { test, expect } from '@playwright/test';
test('週間天気テスト', async ({ page }) => {
await page.goto('https://weather.yahoo.co.jp/weather/jp/13/4410.html');
const trs = await page.locator('.yjw_table tr').all();
const getRowData = async (tr) =>
(await tr.allInnerTexts()).join('').split(/\s+/);
const rowsData = await Promise.all(trs.map(getRowData));
console.log(rowsData);
// [
// [
// '日付', '2月5日',
// '(水)', '2月6日',
// '(木)', '2月7日',
// '(金)', '2月8日',
// '(土)', '2月9日',
// '(日)', '2月10日',
// '(月)'
// ],
// [
// '天気', '晴れ',
// '晴れ', '晴れ',
// '晴れ', '晴れ',
// '晴れ'
// ],
// [
// '気温(℃)', '10',
// '1', '9',
// '-1', '10',
// '0', '9',
// '0', '9',
// '1', '10',
// '1'
// ],
// [ '降水', '確率(%)', '10', '0', '0', '20', '10', '0' ]
// ]
});
expectを使用してテストする際は、以下のようなコードになります。
分割代入をすると、変数名を付けられるため、何のデータかわかりやすくなります。
import { test, expect } from '@playwright/test';
test('週間天気テスト', async ({ page }) => {
await page.goto('https://weather.yahoo.co.jp/weather/jp/13/4410.html');
const trs = await page.locator('.yjw_table tr').all();
const getRowData = async (tr) => (await tr.allInnerTexts()).join('').split(/\s+/);
const [dates, weathers, temps, rains] = await Promise.all(trs.map(getRowData));
const filteredDates = dates.filter((date) => /^\d/.test(date));
console.log(filteredDates);
// ['2月5日', '2月6日', '2月7日', '2月8日', '2月9日', '2月10日']
const isValidDate = (date) => /^\d+月\d+日$/.test(date);
filteredDates.forEach(date => {
expect(isValidDate(date)).toBe(true);
})
});
ちなみに @playwright/test ではなく、tenki.mjsを作成して、import { chromium } from 'playwright' でインポートして、node tenki.mjsコマンドでtableのtdの値を取得する場合は以下のようなコードになります。
import { chromium } from 'playwright';
async function tenki() {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://weather.yahoo.co.jp/weather/jp/13/4410.html');
const trs = await page.locator('.yjw_table tr').all();
const getRowData = async (tr) =>
(await tr.allInnerTexts()).join('').split(/\s+/);
const [dates, weathers, temps, rains] = await Promise.all(trs.map(getRowData));
console.log(dates);
// [
// '日付', '2月5日',
// '(水)', '2月6日',
// '(木)', '2月7日',
// '(金)', '2月8日',
// '(土)', '2月9日',
// '(日)', '2月10日',
// '(月)'
// ]
await context.close();
await browser.close();
}
await tenki();