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

Puppeteer脚本Block 2失效求助:点击未触发页面直接关闭

Puppeteer Block 2's Click Events Ignored, Page Closes Before Media Requests Are Captured

我写了一个Puppeteer脚本,Block 1能正常获取数据并写入文件,但Block 2的元素点击事件完全没执行——本该在page.on('request')里捕获的media请求根本没出现,脚本跑完Block 1后直接关闭页面,Node.js也没返回任何错误。这是怎么回事?

脚本代码:

async function main() { 
  const browser = await puppeteer.launch({headless: false}); 
  const page = await browser.newPage(); 
  await page.setViewport({width: 1200, height: 720}) 
  await page.goto('https://site.local', { waitUntil: 'networkidle0' }); 
  // wait until page load 
  await page.type("input[name='UserName']", "myusername"); 
  await page.type("input[name='Password']", "mypassworduser"); 
  // click and wait for navigation 
  await Promise.all([ 
    page.click("body > main > button"), 
    page.waitForNavigation({ waitUntil: 'networkidle0' }), 
  ]); 
  await page.goto(urlformation); 
  await page.setRequestInterception(true); 
  await page.on('request', (request) => { 
    if (request.resourceType() === 'media') { 
      var CurrentRequest = request.url(); 
      console.log(CurrentRequest); 
      fs.appendFileSync(fichiernurlaudio, request.url()+"\r\n"); 
    } 
    request.continue(); 
  }); 
  //START BLOC 1 ------------------IT WORK 
  const Titresaudios = await page.evaluate(() => { 
    let names = document.querySelectorAll( "td.cursor.audio" ); 
    let arr = Array.prototype.slice.call(names); 
    let text_arr = []; 
    for (let i = 0; i < arr.length; i += 1) { 
      text_arr.push($vartraited+"\r\n"); 
    } 
    return text_arr; 
  }) 
  fs.appendFileSync(fichiernomaudio, Titresaudios); 
  //END BLOCK 1------------------IT WORK- i got data in my file 
  //START BLOCK 2-------seems to ignore-----------NOT WORKING 
  await page.evaluate(()=>{ 
    let elements = document.querySelectorAll("td.cursor.audio"); 
    elements.forEach((element, index) => { 
      setTimeout(() => { 
        element.click(); 
      }, index * 1000); 
    }) 
  }) 
  //END BLOCK 2---------seems to ignore---------NO WORKING 
  //i should see some console.log in page.on('request' (request) => { but instant close after works of bloc 1 
  await page.close(); 
  await browser.close(); 
} 
main();

问题根源

核心问题是Node.js主流程没有等待Block 2里的异步点击操作完成,就直接执行了page.close()和browser.close():

  1. page.evaluate()里的setTimeout是在浏览器的JavaScript上下文中执行的异步操作,但page.evaluate()本身在完成forEach循环后就立即返回并resolve了——它不会等待内部所有setTimeout触发的点击完成。
  2. 主流程在page.evaluate()返回后,立刻走到关闭页面的代码,导致浏览器还没来得及触发那些延迟的点击,页面就被销毁了,自然也捕获不到media请求。

另外还有个小细节:page.on('request')是事件监听器注册,不需要加await(它不返回Promise),虽然这不会导致功能故障,但属于冗余代码。

解决方案

我们需要让Node.js主流程等待所有点击操作和对应的media请求完成后,再关闭页面。这里提供两种可行方案:

方案1:在浏览器上下文返回Promise,等待所有点击完成

修改Block 2的page.evaluate(),让它返回一个Promise,这个Promise会在最后一个点击触发并完成后resolve:

//START BLOCK 2-------FIXED 
await page.evaluate(async ()=>{ 
  let elements = document.querySelectorAll("td.cursor.audio"); 
  // 将forEach转为Promise数组,等待所有延迟点击完成
  await Promise.all(Array.from(elements).map((element, index) => {
    return new Promise(resolve => {
      setTimeout(() => {
        element.click();
        resolve();
      }, index * 1000);
    });
  }));
  // 额外等待1秒,确保最后一个点击触发的media请求完成(可根据实际情况调整时长)
  await new Promise(resolve => setTimeout(resolve, 1000));
}) 
//END BLOCK 2---------FIXED

方案2:在Node.js侧监听media请求,等待所有预期请求完成

如果你知道预期要捕获的media请求数量,可以在Node.js侧设置计数器,等所有请求都捕获到后再继续流程:

// 在注册request监听器前初始化计数器和等待Promise
const expectedMediaRequests = (await page.$$eval("td.cursor.audio", els => els.length));
let mediaRequestCount = 0;
const mediaRequestsDone = new Promise(resolve => {
  if (expectedMediaRequests === 0) resolve();
});

page.on('request', (request) => { 
  if (request.resourceType() === 'media') { 
    const currentRequest = request.url(); 
    console.log(currentRequest); 
    fs.appendFileSync(fichiernurlaudio, `${currentRequest}\r\n`); 
    mediaRequestCount++;
    // 所有预期请求都捕获到后,触发resolve
    if (mediaRequestCount === expectedMediaRequests) {
      mediaRequestsDone.resolve();
    }
  }
  request.continue(); 
});

// ... Block 1代码保持不变 ...

//START BLOCK 2-------FIXED 
await page.evaluate(()=>{ 
  let elements = document.querySelectorAll("td.cursor.audio"); 
  elements.forEach((element, index) => { 
    setTimeout(() => { 
      element.click(); 
    }, index * 1000); 
  }) 
}) 
//END BLOCK 2---------FIXED

// 等待所有media请求完成
await mediaRequestsDone;
// 额外等待500ms确保请求处理完成
await new Promise(resolve => setTimeout(resolve, 500));

// 现在再关闭页面
await page.close(); 
await browser.close();

额外优化建议

  • 尽量避免使用fs.appendFileSync这种同步IO操作,它会阻塞Node.js事件循环,建议改用异步的fs.promises.appendFile。
  • 注意Block 1里的$vartraited变量:如果它是Node.js侧的变量,需要通过参数传递到浏览器上下文,比如page.evaluate((vartraited) => { ... }, $vartraited),否则浏览器侧会找不到这个变量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:52:52