You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Playwright批量抓取漫画章节列表?

解决漫画章节批量抓取与元素CSS选择器生成问题

我正在进行漫画内容抓取工作,目前已成功获取单个章节的数据,但始终无法实现批量抓取所有章节并生成章节数组。此前我通过指定单个章节与章节名称的CSS选择器来获取数据,但这种方式并不适用于批量抓取场景。因此我尝试编写函数,获取每个章节元素内所有元素的CSS选择器并打印至控制台,但该功能始终无法正常运行,特此寻求技术帮助。

你的代码存在几个核心问题导致批量抓取和选择器生成失败:

  • 章节列表选择器错误:原选择器指向的是章节列表的容器而非单个章节项,循环仅执行一次,无法批量获取所有章节
  • CSS选择器生成逻辑缺失:当前仅获取元素的tagName,并非完整可定位的CSS选择器
  • 动态内容等待不充分:目标页面使用astro-island动态渲染内容,仅等待domcontentloaded可能导致章节元素未完全加载

以下是修正后的代码:

import playwright from "playwright";

// 生成元素的完整CSS选择器
const generateCSSSelector = (element) => {
  if (element.tagName === 'HTML') return 'html';
  if (element.tagName === 'BODY') return 'body';

  let selector = element.tagName.toLowerCase();
  if (element.id) {
    selector += `#${element.id}`;
    return selector;
  }

  if (element.className) {
    const classes = element.className.trim().split(/\s+/).join('.');
    selector += `.${classes}`;
  }

  // 计算同级别元素中的位置
  const siblings = Array.from(element.parentElement.children).filter(el => el.tagName === element.tagName);
  if (siblings.length > 1) {
    const index = siblings.indexOf(element) + 1;
    selector += `:nth-child(${index})`;
  }

  return `${generateCSSSelector(element.parentElement)} > ${selector}`;
};

(async () => {
  let browser;
  let context;
  try {
    const url = "https://battwo.com/title/75019-blue-lock";
    const yearUrl = "https://www.anime-planet.com/manga/blue-lock";
    const userAgent =
      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36";

    browser = await playwright.firefox.launch({ headless: true });
    context = await browser.newContext({ userAgent });

    const page = await context.newPage();
    await page.setViewportSize({ width: 1280, height: 720 }); // 调整视口避免移动端布局影响
    page.setDefaultTimeout(180000);

    // 等待页面完全加载,包括动态内容
    await page.goto(url, { waitUntil: "networkidle", timeout: 60000 });

    // 等待astro-island内的章节列表渲染完成
    await page.waitForSelector("main.px-3 astro-island div div.px-2", { visible: true, timeout: 60000 });

    // 抓取年份数据
    const yearPage = await context.newPage();
    await yearPage.goto(yearUrl, { waitUntil: "domcontentloaded", timeout: 60000 });
    await yearPage.waitForSelector("div.pure-1.md-1-5 > span.iconYear", { visible: true });
    const year = await yearPage.$eval("div.pure-1.md-1-5 > span.iconYear", 
      (element) => element.innerText.replace(/[-?]/g, "").trim());
    
    // 获取单个章节项的列表(修正选择器)
    const listItems = await page.$$("main.px-3 astro-island div div.px-2");
    const chapters = [];

    for (const item of listItems) {
      // 获取章节号和名称(简化选择器)
      const chapter = await item.$eval("div > a", (element) => element.innerText.trim());
      const name = await item.$eval("div > span:nth-child(2)", (element) => element.innerText.trim());
      chapters.push({ chapter, name });

      // 生成并打印当前章节元素内所有子元素的CSS选择器
      const selectors = await item.$$eval("*", (elements) => {
        return elements.map(el => generateCSSSelector(el));
      });
      console.log(`章节 ${chapter} 内元素选择器:`, selectors);
    }
    
    const scrapedData = {
      year,
      chapters
    };
    console.log("抓取结果:", scrapedData);
  } catch (e) {
    console.error("抓取错误:", e);
  } finally {
    const delay = Math.floor(Math.random() * (15000 - 8000 + 1)) + 8000;
    await new Promise((resolve) => setTimeout(resolve, delay));
    if (context) await context.close();
    if (browser) await browser.close();
    console.log("抓取完成,浏览器已关闭");
  }
})();

关键修改说明:

  • 修正章节选择器:使用main.px-3 astro-island div div.px-2定位单个章节项,确保listItems包含所有章节元素
  • 添加CSS选择器生成函数:generateCSSSelector递归生成元素的完整路径,包含标签、ID、类名和兄弟元素位置,确保选择器唯一可定位
  • 优化页面等待逻辑:改用networkidle等待页面加载完成,确保动态渲染的章节列表完全加载;调整视口尺寸避免移动端布局影响元素选择
  • 简化章节数据选择器:利用章节项的相对路径获取数据,避免依赖过于冗长的绝对选择器,提升代码稳定性

内容的提问来源于stack exchange,提问作者moises ramirez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 13:24:50