在 Puppeteer 中滚动到 div 底部无效。

9

我想要爬取下面图片框选区域内的所有音乐会:

https://istack.dev59.com/7QIMM.webp

问题在于这个列表只呈现前10个选项,直到你在特定的div中向下滚动到底部,然后它才会动态呈现更多内容,直到没有更多结果为止。 我试着按照以下链接的答案操作,但无法滚动以展示所有的“音乐会”:

如何使用Puppeteer在div内滚动?

这是我的基本代码:

const browser = await puppeteerExtra.launch({ args: [                
    '--no-sandbox'                                                  
    ]});

async function functionName() {
    const page = await browser.newPage();
    await preparePageForTests(page);
    page.once('load', () => console.log('Page loaded!'));
    await page.goto(`https://www.google.com/search?q=concerts+near+poughkeepsie&client=safari&rls=en&uact=5&ibp=htl;events&rciv=evn&sa=X&fpstate=tldetail`);   

    const resultList = await page.waitForSelector(".odIJnf"); 
    const scrollableSection = await page.waitForSelector("#Q5Vznb");    //I think this is the div that contains all the concert items.
    const results = await page.$$(".odIJnf");  //this needs to be iterable to be used in the for loop

//this is where I'd like to scroll down the div all the way to the bottom

    for (let i = 0; i < results.length; i++) {
      const result = await (await results[i].getProperty('innerText')).jsonValue();
      console.log(result)
    }
}
2个回答

3

尝试使用此方法在音乐会列表中向下滚动。您可以不断循环直到结果数量停止增加,或者找到您要查找的音乐会:

await page.evaluate(()=>{
  document.querySelector("#Q5Vznb").scrollIntoView(false);
});

嗨Benny,我认为div可能有问题。到目前为止,我已经尝试了#Q5Vznb、.MZpzq和.uAAqtb,但都没有成功获取超过原始加载的.odIJnf元素数量。你有什么其他div建议吗?谢谢! - nickcoding2
我还尝试使用'#immersive_desktop_root > div.drPJve > div.YbRs3e > div:nth-child(2) > div.UbEfxe.uAAqtb > div.MZpzq.gws-horizon-textlists__tl-no-filters.TWKvJb'和'#immersive_desktop_root > div.drPJve > div.YbRs3e > div:nth-child(2) > div.UbEfxe.uAAqtb'作为querySelector()的参数。不幸的是,两者都没有起作用。 - nickcoding2
我认为它是正确的。我刚刚在Google Chrome控制台中访问了该网站,并使用以下操作(右键单击,然后单击检查; 或使用快捷键Ctrl + Shift + I):document.querySelectorAll('.odIJnf').length >> 20 document.querySelector("#Q5Vznb").scrollIntoView(false); >> undefined document.querySelectorAll('.odIJnf').length
30
所以,在滚动命令后,音乐会数量增加了10。
- Benny
我本以为它们都会返回元素数组。page.$$(“.odIJnf”)可遍历,我最终在for循环中使用await(await results[i].getProperty('innerText')).jsonValue()访问它; --由于某种原因,document.querySelectorAll() 版本不能被用于迭代。我看到这是因为它返回一个对象,有没有办法让它返回与$$函数返回相同的内容? - nickcoding2
你觉得呢? - nickcoding2
显示剩余4条评论

2
正如您在问题中提到的那样,当您运行page.$$时,会返回一个ElementHandle数组。根据Puppeteer文档

ElementHandle表示页面内的DOM元素。可以使用page.$方法创建ElementHandle。

这意味着您可以对它们进行迭代,但是您还必须对每个元素运行evaluate()$eval()才能访问DOM元素。
我从您的代码片段中看到,您正在尝试访问处理列表scroll事件的父div。问题在于,该页面似乎正在使用自动生成的classesids。这可能会使您的代码变得脆弱或无法正常工作。最好尝试直接访问ullidiv
我创建了此代码片段,可以从网站获取ITEMS场音乐会:
const puppeteer = require('puppeteer')

/**
 * Constants
 */
const ITEMS = process.env.ITEMS   || 50
const URL   = process.env.URL     || "https://www.google.com/search?q=concerts+near+poughkeepsie&client=safari&rls=en&uact=5&ibp=htl;events&rciv=evn&sa=X&fpstate=tldetail"

/**
 * Main
 */
main()
  .then( ()    => console.log("Done"))
  .catch((err) => console.error(err))

/**
 * Functions
 */
async function main() {
  const browser = await puppeteer.launch({ args: ["--no-sandbox"] })
  const page = await browser.newPage()
  
  await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36')
  await page.goto(URL)
 
  const results = await getResults(page)
  console.log(results)
  
  await browser.close()
}

async function getResults(page) {
  await page.waitForSelector("ul")
  const ul  = (await page.$$("ul"))[0]
  const div = (await ul.$x("../../.."))[0]
  const results = []
  
  const recurse = async () => {
    // Recurse exit clause
    if (ITEMS <= results.length) {
      return
    }

    const $lis = await page.$$("li")
    // Slicing this way will avoid duplicating the result. It also has
    // the benefit of not having to handle the refresh interval until
    // new concerts are available.
    const lis = $lis.slice(results.length, Math.Infinity)
    for (let li of lis) {
      const result = await li.evaluate(node => node.innerText)
      results.push(result)
    }
    // Move the scroll of the parent-parent-parent div to the bottom
    await div.evaluate(node => node.scrollTo(0, node.scrollHeight))
    await recurse()
  }
  // Start the recursive function
  await recurse()
 
  return results
}

通过研究页面结构,我们发现列表的 ul 嵌套在三个 div 中,深度从处理 scrolldiv 开始。我们还知道页面上只有两个 ul,而第一个是我们想要的。这就是以下代码的作用:
  const ul  = (await page.$$("ul"))[0]
  const div = (await ul.$x("../../.."))[0]

$x函数相对于文档作为其上下文节点来评估XPath表达式。它允许我们遍历DOM树,直到找到我们需要的div。然后我们运行一个递归函数,直到获得我们想要的项目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接