Puppeteer 在当前页面或 iFrame 中查找元素

Posted

技术标签:

【中文标题】Puppeteer 在当前页面或 iFrame 中查找元素【英文标题】:Puppeter find element in current page OR iFrame 【发布时间】:2021-09-02 05:13:24 【问题描述】:

我的页面中有一些元素有时在 iFrame 中,有时则不在。 在这两种情况下,它们具有相同的 id 来识别它们。

在 page 或 iFrame 中找到它们的最佳且快速的方法是什么?

我目前使用的代码在元素位于 iFrame 中时有效,但如果元素仅在页面中则无效。 我以为mainFrame会是iFrame之外的“currentPage”,但似乎代码搜索只在iFrame中,而不是在外面。

async function recursiveFindInFrames(inputFrame, selector) 
  const frames = inputFrame.childFrames();
  const results = await Promise.all(
    frames.map(async frame => 
      const el = await frame.$(selector);
      if (el) return el;
      if (frame.childFrames().length > 0) 
        return await recursiveFindInFrames(frame, selector);
      
      return null;
    )
  );
  return results.find(Boolean);


async function findInFrames(page, selector) 
  const result = await recursiveFindInFrames(page.mainFrame(), selector);
  if (!result) 
    console.log('The selector ',selector,' could not be found in any child frames.')
  
  return result;

...
//Element that could be in an iFrame, or not
const element = await findInFrames(page2, 'input[name="myField"]');
await element.click();
...

【问题讨论】:

【参考方案1】:

当任何元素在同一个页面中时,它肯定会更容易找到,而且您不需要特殊的功能来做到这一点。

如果您知道元素的 id,这就足够了:$('#element_id')(如果元素的 id 没有硬编码,您也可以使用变量)。

【讨论】:

【参考方案2】:

您不能添加page.evaluatequerySelector 吗?

async function findInFrames(page, selector) 
  const framesResult = await recursiveFindInFrames(page.mainFrame(), selector);
  if (!framesResult) 
    console.log('The selector ',selector,' could not be found in any child frames.')
  
  const pageResult = await page.evaluate(() => document.querySelector(selector))
  return framesResult || pageResult || null; 

【讨论】:

【参考方案3】:

使用frames 方法代替childFrames。 所以这段代码

  const frames = inputFrame.childFrames();

应该这样写:

   const frames = inputFrame.frames();

frames 方法也以数组的形式返回 MainFrame。因此,该函数也会在 mainFrame 上进行搜索。

【讨论】:

【参考方案4】:

要检查除嵌套子框架之外的主页,您可以在遍历子框架之前先检查inputFrame。它可能看起来像这样:

async function recursiveFindInFrames(inputFrame, selector) 
  var el = await inputFrame.$(selector);
  if (el) 
     return el;
  
  const childFrames = inputFrame.childFrames();
  for (let i = 0; i < childFrames.length; i++) 
    el = await recursiveFindInFrames(childFrames[i], selector);
    if (el) 
      return el;
    
  
  return null;


async function findInFrames(page, selector) 
  const result = await recursiveFindInFrames(page.mainFrame(), selector);
  if (!result) 
    console.log('The selector, ' + selector + ', could not be found in any child frames.')
  
  return result;

...
//Element that could be in an iFrame, or not
const element = await findInFrames(page2, 'input[name="myField"]');
if (element) 
  await element.click();

...

请注意,此方法按顺序搜索,等待每一帧的结果。如果你想并行搜索子框架,你可以这样做:

async function recursiveFindInFrames(inputFrame, selector) 
  var el = await inputFrame.$(selector);
  if (el) 
     return el;
  
  const childFrames = inputFrame.childFrames();
  const elementPromises = childFrames.map(frame => recursiveFindInFrames(frame, selector));
  for (let i = 0; i < elementPromises.length; i++) 
    el = await elementPromises[i];
    if (el) 
      return  el;
    
  
  return null;

也就是说,我认为潜在的改进非常小。

【讨论】:

以上是关于Puppeteer 在当前页面或 iFrame 中查找元素的主要内容,如果未能解决你的问题,请参考以下文章

Puppeteer 截取上一个页面而不是当前页面

获取在 Puppeteer 中悬停的链接的 href

Puppeteer爬取Youtube列表页面

Puppeteer:为什么textarea中的值返回

如何在iframe子页面添加tabs

如何在子页面得到其所在的iframe