/** * # 脚本 取证 posts widget 容器结构(本地 vs 原站,390px) * 抓每个 posts 区块的 widget data-id、grid/容器样式、article 数、swiper 迹象。 * 用法:bun scripts/forensic-posts-struct.ts */ import { chromium } from "playwright-core"; const CHROME = process.env.HOME + "/.cache/ms-playwright/chromium-1243/chrome-linux64/chrome"; const LOCAL = "http://127.0.0.1:3312"; const LIVE = "https://www.dusuniot.com"; const path = process.argv[2] || "/"; async function measure(base: string) { const browser = await chromium.launch({ executablePath: CHROME, headless: true }); const page = await browser.newPage({ viewport: { width: 390, height: 844 } }); await page.goto(base + path, { waitUntil: "networkidle", timeout: 60000 }); await page.evaluate(async () => { for (let y = 0; y < document.body.scrollHeight; y += 600) { window.scrollTo(0, y); await new Promise((r) => setTimeout(r, 100)); } }); await page.waitForTimeout(1500); const data = await page.evaluate(() => { const out: unknown[] = []; for (const grid of Array.from(document.querySelectorAll(".elementor-posts-container"))) { const widget = grid.closest("[data-id]") as HTMLElement | null; const art = grid.querySelectorAll("article"); const cs = getComputedStyle(grid); const wrap = widget?.parentElement; out.push({ widgetId: widget?.getAttribute("data-id"), widgetClass: (widget?.className || "").slice(0, 120), wrapClass: (wrap?.className || "").slice(0, 140), articles: art.length, gridDisplay: cs.display, gridCols: (grid.getAttribute("style") || "").slice(0, 160), swiper: !!widget?.querySelector(".swiper"), articleW: art.length ? +art[0].getBoundingClientRect().width.toFixed(1) : 0, }); } return out; }); await browser.close(); return data; } const [local, live] = await Promise.all([measure(LOCAL), measure(LIVE)]); console.log("=== 本地 ==="); for (const d of local) console.log(JSON.stringify(d)); console.log("=== 原站 ==="); for (const d of live) console.log(JSON.stringify(d));