You need to enable JavaScript to run this app. Puppeteer 如何批量点击页面所有同选择器按钮?解决仅触发首个按钮问题
优惠活动
大模型
产品
解决方案
定价
更多

Puppeteer 如何批量点击页面所有同选择器按钮?解决仅触发首个按钮问题

Puppeteer批量点击YouTube展开按钮失效问题修复

失效原因拆解

  • 选择器匹配错误:你当前写的#button-container > #show-more-button > a > #button不符合YouTube实际DOM结构,真实的展开按钮没有外层a标签包裹,多层嵌套的选择器无法稳定匹配目标元素,仅能偶尔命中第一个随机匹配的节点。
  • 点击时序冲突:在单次page.evaluate内同步遍历点击元素时,YouTube为响应式渲染架构,首次点击触发区块内容加载后,原有DOM节点会被重新渲染替换,后续循环持有的旧元素引用早已失效,无法触发有效点击。
  • 缺少渲染等待间隙:点击展开按钮后未设置等待时间,页面异步加载的新视频内容还没渲染完成就直接执行采集逻辑,即使点击生效也无法抓到新增条目。
  • 前置等待逻辑不严谨:代码中page.waitForSelector('#button')的选择器过于宽泛,会匹配页面上所有带id为button的元素,无法精准等待目标组件加载完成,容易触发超时或提前进入采集流程;提前定义的navigationPromise由于创建时机早于page.goto(),会出现导航事件已经触发完成、再等待就永远不返回的竞态问题,易导致流程卡死。

可运行的修正实现

async function scrape(url) {
  const browser = await puppeteer.launch({
    headless: false,
    args: ['--start-maximized']
  });
  const page = await browser.newPage();
  await page.setViewport({ width: 1200, height: 800 });

  await page.goto(url, { timeout: 0, waitUntil: 'networkidle2' });

  // 滚动加载初始内容
  await page.evaluate(_ => window.scrollBy(0, window.innerHeight));
  await page.waitForSelector('ytd-button-renderer#show-more-button button#button', { timeout: 10000 });

  let lastBtnCount = 0;
  // 循环展开所有内容直到无新的可点击按钮
  while (true) {
    // 拿到当前页面所有可点击的"显示更多"按钮,过滤已经变成"收起"状态的节点
    const currentBtnCount = await page.evaluate(() => {
      const showMoreBtns = Array.from(document.querySelectorAll('ytd-button-renderer#show-more-button button#button'));
      // 仅筛选状态为"显示更多"的按钮,排除已点击后变成"收起"的节点
      const validBtns = showMoreBtns.filter(btn => 
        btn.innerText.trim() === '显示更多' || btn.innerText.trim() === 'Show more'
      );
      // 逐个点击有效按钮
      validBtns.forEach(btn => btn.click());
      return validBtns.length;
    });

    // 等待页面加载点击后新增的内容
    await page.waitForTimeout(800);
    // 滚动到页面底部加载更多潜在的展开按钮
    await page.evaluate(_ => window.scrollBy(0, document.body.scrollHeight));
    await page.waitForTimeout(500);

    // 连续两次无新增可点击按钮,跳出循环开始采集
    if (currentBtnCount === 0 && currentBtnCount === lastBtnCount) break;
    lastBtnCount = currentBtnCount;
  }

  // 所有内容展开完成后统一采集信息
  const textSelector = 'h3 > a > #video-title';
  const srcSelector = 'ytd-thumbnail > a > yt-img-shadow > #img';
  await page.waitForSelector(textSelector);
  await page.waitForSelector(srcSelector);

  const ytTextData = await page.$$eval(textSelector, elems => 
    [...new Set(elems.map(el => el.textContent.trim()))].join('\n')
  );
  const ytSrcData = await page.$$eval(srcSelector, elems => 
    [...new Set(elems.map(el => el.src))].join('\n')
  );

  console.log({ ytTextData, ytSrcData });
  await browser.close();
}

关键优化说明

  • 替换了精准的按钮选择器,匹配YouTube实际渲染的展开按钮节点,避免选择器匹配失效的问题。
  • 调整点击逻辑为逐轮检测、逐轮点击,每轮点击后等待渲染完成、滚动加载潜在新按钮,直到无新的可点击展开按钮再停止,解决旧元素引用失效的问题。
  • 增加按钮状态过滤逻辑,点击时仅匹配状态为“显示更多”的节点,避免误把已经展开的区块重新收起。
  • 采集环节增加去重逻辑,避免因为页面懒加载导致的重复条目采集。
  • 调整了页面加载等待策略,使用networkidle2替代硬编码的5秒等待,在页面网络请求完成后再启动流程,提升运行效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 15:06:04