在 puppeteer 中滚动到 div 的底部不起作用

nic*_*ng2 8 javascript node.js web-scraping infinite-scroll puppeteer

所以我试图在下图中的封闭区域中刮取所有音乐会:

https://i.stack.imgur.com/7QIMM.jpg

问题是列表只显示前 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

guz*_*nne 2

正如您在问题中提到的,当您运行 时page.$$,您会返回一个ElementHandle. 来自Puppeteer 的文档

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

这意味着您可以迭代它们,但您还必须运行evaluate()$eval()遍历每个元素才能访问 DOM 元素。

我从您的代码片段中看到您正在尝试访问div处理列表scroll事件的父级。问题是该页面似乎正在使用自动生成的classesids. 这可能会使您的代码变得脆弱或无法正常工作。最好尝试直接访问ul, li, 。div

我创建了这个片段,可以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
}
Run Code Online (Sandbox Code Playgroud)

通过研究页面结构,我们看到ul列表的 嵌套在距处理 的3div层深处。我们也知道页面上只有两个,第一个就是我们想要的。这就是我们在这些方面所做的:divscrollul

  const ul  = (await page.$$("ul"))[0]
  const div = (await ul.$x("../../.."))[0]
Run Code Online (Sandbox Code Playgroud)

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