import {chromium} from 'playwright'; const b = await chromium.launch({channel: 'chrome'}); const page = await b.newPage({viewport: {width: 1280, height: 900}}); await page.goto('https://web4ai.o2osolution.ai/s/stay', {waitUntil: 'domcontentloaded'}); await page.waitForTimeout(5000); const r = await page.evaluate(() => { const li = document.querySelector('#itinerary ol li'); const title = li.querySelector('span.block.text-sm, span[class*="text-sm"][class*="font-bold"]'); const badge = title.querySelector('span'); const rb = badge.getBoundingClientRect(); const rt = title.getBoundingClientRect(); // 제목 첫 글자의 실제 상자 const node = [...title.childNodes].find((n) => n.nodeType === 3 && n.textContent.trim()); const rng = document.createRange(); rng.selectNodeContents(node); const rr = rng.getBoundingClientRect(); const cs = getComputedStyle(title); return { badge: {top: +rb.top.toFixed(2), h: +rb.height.toFixed(2), mid: +(rb.top + rb.height / 2).toFixed(2)}, textRect: {top: +rr.top.toFixed(2), h: +rr.height.toFixed(2), mid: +(rr.top + rr.height / 2).toFixed(2)}, line: {fontSize: cs.fontSize, lineHeight: cs.lineHeight, display: getComputedStyle(badge).display, va: getComputedStyle(badge).verticalAlign, tf: getComputedStyle(badge).transform}, text: title.textContent.trim().slice(0, 20), delta: +((rb.top + rb.height / 2) - (rr.top + rr.height / 2)).toFixed(2), }; }); console.log(JSON.stringify(r, null, 1)); await b.close();